Introduction

Motivation

Indego is the de facto bike sharing network in Philadelphia. It has become a popular, more eco-friendly alternative for short trips across the city since launching in late April of 2015. However, little has been done to analyze the publicly available data associated with Indego. Thus, we think it would be beneficial to the public if we quantify and assess several pertinent features and patterns related to Indego, generating insights on how the bike sharing network is utilized across the city. More specifically, we want to more closely study and answer questions related to the following four themes:

  • Station usage (usage rate, peak periods, popular rental/return stations, etc.)
  • Daily traffic (popular routes, volume across different sections, travel patterns, etc.)
  • Trip durations (length of trips, median rental/return time, etc.)
  • User profiles (type of passholders, frequency of round-trips, seasonality, etc.)

Each of the above four features will be explained in finer details in subsequent sections. To realize this objective, we have created static plots (shown in this notebook) to describe salient themes as well as a Shiny app (Github), which also contains real-time status updates of all Indego stations across Philadelphia. We will also provide instructions on runnning our app further down in our report.

Our objective starting out was to examine bike share programs in the most densely populated cities across the eastern United States, mainly via exploratory data analysis and visualization techinques. We initially considered NYC’s Citibike as a promising candidate, but it has already been extensively analyzed (see Todd Schneider’s blogpost for an excellent example). The marginal returns of analyzing such a dataset are thus minimal, if not, nil. As a result, we decided on Philadelphia, primarliy for two reasons: it is the second largest city satisfying our predefined criteria (after NYC), and also has a relatively new bike share program. Thus, we thought the Indego dataset would be an ideal choice. It would also be interesting to use our dataset to compare and contrast New York and Philadelphia’s bike share programs.

Data Source

The dataset is publicly available on Indego’s website. It can be found by clicking the About panel and then selecting Data. All quarterly data are listed under the section The Data, in which each station’s information is available through the Station Table hyperlink. In addition, real-time data used in our Shiny app is available under the section Indego Station Status via the GeoJSON hyperlink.

Team

  1. Kejia Shi (ks3403): data processing, exploratory data analysis, visualization (static and Shiny plots), writing/editing report.
  2. Mason Sun (zs2321): data processing, exploratory data analysis, developing front/backend of Shiny app, writing/editing report.
  3. Hung-Shi Lin (hl2997): proofread.

Analysis of Data Quality

Quarterly data

For quarterly data, we had seven files in total, each representing data between Q2 of 2015 and Q4 of 2016, inclusively. Each contained the same columns:

  • trip_id (unique identifier of a bike trip)
  • duration (length of trip in seconds or minutes calculated by check-in and check-out time)
  • start_time (clock start time and calendar date)
  • end_time (clock end time and calenar date)
  • start_station_id (unique identifier of starting station)
  • start_lat (latitude of starting station)
  • start_lon (longitude of starting station)
  • end_station_id (unique identifier of end station)
  • end_lat (latitude of end station)
  • end_lon (longitude of end station)
  • bike_id (unique identifier of the bike used during the trip)
  • plan_duration (type of pass by number of days, e.g. thirty-day pass is shown as 30)
  • trip_route_category (round trip or one-way)
  • passholder_type (walkup, IndegoFlex, or Indego30)

We combined these into one data frame (called df) using the code as follows. The description of our preprocessing methods are embedded as comments in the code chunk below. Please see them for more detailed explanations.

library(tidyverse)
### read in quarterly data ###
Y16Q4 <- read.csv("data/Indego_Trips_2016Q4.csv", header=T)
Y16Q3 <- read.csv("data/Indego_Trips_2016Q3.csv", header=T)
Y16Q2 <- read.csv("data/Indego_Trips_2016Q2.csv", header=T)
Y16Q1 <- read.csv("data/Indego_Trips_2016Q1.csv", header=T)
Y15Q4 <- read.csv("data/Indego_Trips_2015Q4.csv", header=T)
Y15Q3 <- read.csv("data/Indego_Trips_2015Q3.csv", header=T)
Y15Q2 <- read.csv("data/Indego_Trips_2015Q2.csv", header=T)
indego_stations <- read.csv("data/indego_stations.csv", header=T)
### preprocessing ###
# select currently active stations from most recent quarterly dataset
station_loc <- Y16Q4 %>% dplyr::select(start_station_id, start_lat, start_lon) %>% unique
# create a dataframe that includes the relevant columns for all active stations by left joining on the active stations and all stations, ignoring any rows that contain missing values
station_loc <- dplyr::left_join(station_loc, indego_stations, by = c("start_station_id" = "Station.ID")) %>% na.omit %>% .[, -6]
# rename columns of dataframe
colnames(station_loc) <- c("id", "lat", "long", "name", "date")
# remove whitespaces in the date column
station_loc$date = trim(station_loc$date %>% as.character())

# below are two auxiliary fuction to parse the start/end time and date of each trip, which displays them as a single string. We partition them into separate columns that now display year, month, day, hour, minutes, and date (YYYY-MM-DD).

# for quarters of 2015
parseTime_15 <- function(df) {
  df$s_hour <- format(as.POSIXct(strptime(df$start_time,"%m/%d/%y %H:%M",tz="")) ,format = "%H")
  df$s_min <- format(as.POSIXct(strptime(df$start_time,"%m/%d/%y %H:%M",tz="")) ,format = "%M")
  df$e_hour <- format(as.POSIXct(strptime(df$end_time,"%m/%d/%y %H:%M",tz="")) ,format = "%H")
  df$e_min <- format(as.POSIXct(strptime(df$end_time,"%m/%d/%y %H:%M",tz="")) ,format = "%M")
  df$year <- format(as.POSIXct(strptime(df$start_time,"%m/%d/%y %H:%M",tz="")) ,format = "%Y")
  df$month <- format(as.POSIXct(strptime(df$start_time,"%m/%d/%y %H:%M",tz="")) ,format = "%m")
  df$day <- format(as.POSIXct(strptime(df$start_time,"%m/%d/%y %H:%M",tz="")) ,format = "%d")
  df$date <- format(as.POSIXct(strptime(df$start_time,"%m/%d/%y %H:%M",tz="")) ,format = "%Y-%m-%d")
  return(df)
}

# for quarters of 2016, which are defined differently from 2015
parseTime_16 <- function(df) {
  df$s_hour <- format(as.POSIXct(strptime(df$start_time,"%m/%d/%Y %H:%M",tz="")) ,format = "%H")
  df$s_min <- format(as.POSIXct(strptime(df$start_time,"%m/%d/%Y %H:%M",tz="")) ,format = "%M")
  df$e_hour <- format(as.POSIXct(strptime(df$end_time,"%m/%d/%Y %H:%M",tz="")) ,format = "%H")
  df$e_min <- format(as.POSIXct(strptime(df$end_time,"%m/%d/%Y %H:%M",tz="")) ,format = "%M")
  df$year <- format(as.POSIXct(strptime(df$start_time,"%m/%d/%Y %H:%M",tz="")) ,format = "%Y")
  df$month <- format(as.POSIXct(strptime(df$start_time,"%m/%d/%Y %H:%M",tz="")) ,format = "%m")
  df$day <- format(as.POSIXct(strptime(df$start_time,"%m/%d/%Y %H:%M",tz="")) ,format = "%d")
  df$date <- format(as.POSIXct(strptime(df$start_time,"%m/%d/%Y %H:%M",tz="")) ,format = "%Y-%m-%d")
  return(df)
}

# pass into respective auxiliary functinos
Y15Q2 <- parseTime_15(Y15Q2)
Y15Q3 <- parseTime_15(Y15Q3)
Y15Q4 <- parseTime_15(Y15Q4)
Y16Q1 <- parseTime_16(Y16Q1)
Y16Q2 <- parseTime_16(Y16Q2)
Y16Q3 <- parseTime_16(Y16Q3)
Y16Q4 <- parseTime_16(Y16Q4)

# combine all quarterly data into a single data frame called df
df <- rbind(Y15Q2, Y15Q3, Y15Q4, Y16Q1, Y16Q2, Y16Q3, Y16Q4)

# standardize duration into units of seconds
# also removes columns not needed in subsequent analyses
dataClean <- function(df) {
  df$duration <- df$duration / 60
  df[, -c(1, 3, 4, 6, 7, 9, 10, 12)]
}
df <- dataClean(df)

# remove individual dataframes
rm(Y15Q2, Y15Q3, Y15Q4, Y16Q1, Y16Q2, Y16Q3, Y16Q4)

During our initial exploration of the dataset, we realized some of the respective datasets for the quarterly data contained missing values, which would imply that our final dataframe also contains missing values. We thus try to qualitatively visualize these features, show below:

# ignore columns after passholder_type (col_num = 14) as subsequent columns are defined by us.
library(extracat)
visna(df[,c(1:14)])

missing_table = table(is.na(df))
cat("Percentage of missing entries:", missing_table[2]/sum(missing_table)*100)
Percentage of missing entries: 0.01720519

We see that the proportion of missing values is miniscule relative to our whole dataset (approximately 0.0172%). Moreover, they are all related to latitude and longitude data as well as the bike ID. If need be, we can infer the latitude and longitude from the station name or station ID, but we actively chose to ignore rows corresponding to these missing values since we cannot explicate why these values are missing. We thus choose to ignore them altogether.

Indego stations data

For the data detailing the information of each station (indego_stations.csv), we had to perform a bit of data preprocessing to get it into an ideal format. The data contained the columns as follows:

  • Station ID (unique identifier of a Indego station)
  • Station Name (name of corresponding Indego station, usually a street address)
  • Go live date (the date that the station started operations)
  • Status (its current status, either active or inactive)

There were many leading and trailing whitespaces in the Station Name column, so performed the following to remove these unneeded characters:

# load stations data
indego_stations <- read.csv("../data/indego_stations.csv", header=T)
# a function that removes leading and trailing white spaces of a given string
trim <- function (x) gsub("^\\s+|\\s+$", "", x)
# remove whitespaces surrounding each entry in the column of Station.Name
indego_stations$Station.Name <- trim(indego_stations$Station.Name)

There were no missing values in this table, so we do not provide plots related to this here.

GeoJSON

For real-time status updates of Indego stations, we use the semi-structured GeoJSON dataset provided by Indego. We allow the user to manually refresh our Shiny app, in which we call the GeoJSON file in real-time to get a visualization update (more specific details in subsequent sections). We used the R packpage geojsonio to unpack and transform this data into a workable format as follows:

library(geojsonio)
station_status <- fromJSON("https://gbfs.bcycle.com/bcycle_indego/station_status.json")

Executive Summary

The primary objective of this report is to derive valuable insights of deploying shared bikes for big cities (in our case, Philadelphia’s Indego), and how they are utilized by the public mass. More specifically, we want to understand patterns of usage among different stations and users over time. The exploratory methodologies that we implement on the Indego dataset will generally follow conventional principles of analyzing temporal and spatial features of large datasets.

Upon retrieving the dataset, we immediately raised the following questions:

  1. For the total 105 stations deployed gradually within two years, which stations are the most popular ones? Has the popularity changed over time?

  2. For specific stations we have an expection on their daily rental patterns. How do we visualize and examine such patterns?

  3. With the geographical data of stations and GeoSON file, how do we draw connections among stations and see rental and return patterns, popular routes, and visualize the real-time station status?

We present here a quick run-through of the plots we present in our main analysis, where we will dive into much finer details. The following plots demonstrate the main findings of our exploratory data analysis.

Firstly, we make general line plots with monthly frequency data and further separate the data into week days and weekends. From the plots, we see clearly the seasonal usage variation.

Secondly, we pick three different stations located at the university area, downtown and suburban Philadelphia, and draw the average hourly trip durations for week days and weekends in a selected time period. Travel patterns among different types of stations thus becomes evident. For example, the weekends’ average trip duration in the afternoon is longer than that of the weekday’s for users close to university campuses.

Thirdly, we utilize leaflet to create interactive maps in order to represent the spatial information we can derive from the dataset. We effectively represented the average daily rental and return frequencies among stations using different dot sizes (larger dots implies higher frequency). We embed the real-time GeoJSON file into our Shiny app to show real-time updates of station status. In order to jointly analyze the spatial and time dimensions, we pick the ten most popular rental and return stations for two recent quarters to determine differences in trends. Besides the plots below, we also made a map for the five most popular routes (see next part).

Moreover, we analyze the daily trip durations and the user profiles, and uncover the the shift of passholder types and trip route (one-way or round-trip) changes. The most incisive observation we got from this section is that the percentage of the most popular passholder type (Indego30) and one-way trips has gotten more popular. In addition, less people are taking round trips.

Lastly, we create a Shiny dashboard to let people explore the dataset as their wishes. That is, they can select stations and date ranges of interest, and also modify parameters of different charts (e.g. number of bins for histograms) to generate customized insights.

Main analysis

In this main section, we present our findings of different facets pertaining to the Indego dataset. We subdivide our analysis into four interrelated sections: station usage, daily traffic, trip duration, and user profiles, in that particualr order. We will walk the reader through our thought process and findings, and finally present some concluding remarks.

Station usage

In this section, we examine the usage rate among the different Indego stations across Philadelphia. We motivate our analysis from a holistic perspective towards more granular analysis. This structural layout also holds true for subsequent sections. With that in mind, let us first patterns related to some of the more popular Indego stations. We subdivide our analysis to that of rental and return stations, which reflect popular starting and terminal destinations that people elect to choose in Philadelphia.

# Popular Stations - histogram
library(gridExtra)
popular_station <- df %>% group_by(year, month, start_station_id) %>% summarize(rental = n())
colnames(popular_station)[3] <- "id"
popular_station$id <- as.character(popular_station$id)
foo <- df %>% group_by(year, month, end_station_id) %>% summarize(return = n())
colnames(foo)[3] <- "id"
foo$id <- as.character(foo$id)
popular_station <- popular_station %>% full_join(foo, by = c("id" = "id", "year" = "year", "month" = "month"))
rm(foo)
popular_station <- tidyr::gather(popular_station, type, count, c(4,5))
colnames(popular_station)[3] <- "id"
popular_station[is.na(popular_station)] <- 0
popular_station <- popular_station[popular_station$id!="#N/A",]
station_loc$id <- as.character(station_loc$id)
reduced_station <- popular_station %>% left_join(station_loc, by=c("id"="id")) %>% na.omit
reduced_station$quarter <- (as.numeric(as.character(reduced_station$month)) - 1) %/% 3 + 1
reduced_station <- reduced_station %>% group_by(id, name, lat, long, type, year, quarter) %>% summarize(count = sum(count))
# Earliest quarter: 2015Q2, Latest quarter: 2016Q4
reduced_station$year <- as.numeric(reduced_station$year)
rs_first_rental <- reduced_station[reduced_station$year==2015&reduced_station$quarter==2&reduced_station$type=="rental",]
rs_last_rental <- reduced_station[reduced_station$year==2016&reduced_station$quarter==4&reduced_station$type=="rental",]
rs_first_return <- reduced_station[reduced_station$year==2015&reduced_station$quarter==2&reduced_station$type=="return",]
rs_last_return <- reduced_station[reduced_station$year==2016&reduced_station$quarter==4&reduced_station$type=="return",]
rs_first_rental <- rs_first_rental[order(-rs_first_rental$count),][1:10,]
rs_last_rental <- rs_last_rental[order(-rs_last_rental$count),][1:10,]
rs_first_return <- rs_first_return[order(-rs_first_return$count),][1:10,]
rs_last_return <- rs_last_return[order(-rs_last_return$count),][1:10,]
rs_first_rental$name <- factor(rs_first_rental$name, levels = rs_first_rental$name[order(rs_first_rental$count)])
rs_last_rental$name <- factor(rs_last_rental$name, levels = rs_last_rental$name[order(rs_last_rental$count)])
rs_first_return$name <- factor(rs_first_return$name, levels = rs_first_return$name[order(rs_first_return$count)])
rs_last_return$name <- factor(rs_last_return$name, levels = rs_last_return$name[order(rs_last_return$count)])
p1 <- ggplot(rs_first_rental, aes(x = name, y = count)) +
  geom_bar(stat = 'identity', color="blue", fill="blue") +
  geom_text(aes(label=count), hjust=-0.05, size=2.5) +
  coord_flip() +
  ggtitle("Most Popular Rental Station 2015Q2") +
  xlab("Frequency") +
  ylab("Station") +
  scale_color_fivethirtyeight("cyl") +
  theme_fivethirtyeight() +
  theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm"))
p2 <- ggplot(rs_last_rental, aes(x = name, y = count)) +
  geom_bar(stat = 'identity', color="blue", fill="blue") +
  geom_text(aes(label=count), hjust=-0.05, size=2.5) +
  coord_flip() +
  ggtitle("Most Popular Rental Station 2016Q4") +
  xlab("Frequency") +
  ylab("Station") +
  scale_color_fivethirtyeight("cyl") +
  theme_fivethirtyeight() +
  theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm"))
p3 <- ggplot(rs_first_return, aes(x = name, y = count)) +
  geom_bar(stat = 'identity', color="blue", fill="blue") +
  geom_text(aes(label=count), hjust=-0.05, size=2.5) +
  coord_flip() +
  ggtitle("Most Popular Return Station 2015Q2") +
  xlab("Frequency") +
  ylab("Station") +
  scale_color_fivethirtyeight("cyl") +
  theme_fivethirtyeight() +
  theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm"))
p4 <- ggplot(rs_last_return, aes(x = name, y = count)) +
  geom_bar(stat = 'identity', color="blue", fill="blue") +
  geom_text(aes(label=count), hjust=-0.05, size=2.5) +
  coord_flip() +
  ggtitle("Most Popular Return Station 2016Q4") +
  xlab("Frequency") +
  ylab("Station") +
  scale_color_fivethirtyeight("cyl") +
  theme_fivethirtyeight() +
  theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm"))
p1 <- ggplot_gtable(ggplot_build(p1))
p2 <- ggplot_gtable(ggplot_build(p2))
p3 <- ggplot_gtable(ggplot_build(p3))
p4 <- ggplot_gtable(ggplot_build(p4))
maxWidth = grid::unit.pmax(p1$widths[2:3], p2$widths[2:3], p3$widths[2:3], p4$widths[2:3])
p1$widths[2:3] <- maxWidth
p2$widths[2:3] <- maxWidth
p3$widths[2:3] <- maxWidth
p4$widths[2:3] <- maxWidth
# Plot histograms for selected quarters
grid.arrange(p1, p3, p2, p4, heights = c(3, 3))

In the above graph, we see that the most popular stations are somewhat invariant to time. We chose the first and latest quarters to determine most popular rental and return stations. In all cases, the most popular rental and return station was Rittenhouse Square. This is intuitive as it is not only a popular tourist destination, being hailed as “one of the finest urban public spaces in the United States,” (at least according to Wikipedia) but it is also in close proximity to the epicenter of Philadelphia. More specifically, in Q2 of 2015, the top five most popular rental and return stations were all the same, with the exact same ranking. Thus, it would seem that there is no preference for dropoff or pickup stations, but simply hubs for bike users. In Q4 of 2016, we observe many of the same return and and rental stations, but differences exist. For instance, Philadelphia Museum of Art (PMOA) is no longer in the top ten most popular return or rental stations whereas University City station is, which was not in Q2 of 2015. These variability are difficult to explain based on the dataset alone, but we surmise a couple of possible causes. First, the demographic of Indego users has shifted over time (from intrigued users to a those using it for routine trips). Second, seasonality may affect popular rental/return stations. There is a larger tourist cohort during the summer so that may be why PMOA is so high up in the rankings for Q2 of 2015. These are all conjectural, and it may serve as an interesting avenue for further exploration. However this is beyond the scope of our project.

We postulate that the popularity of certain stations across time is due to the fact they are in areas of heavy pedestrian traffic (e.g. city centers), and they are in close proximity to one another but outside comfortable walking range. We thus put this idea to the test by considering spatial patterns and distances amongst these more popular stations.

# Popular stations - maps
rs_first_return$type <- "Return"
rs_last_return$type <- "Return"
rs_first_rental$type <- "Rental"
rs_last_rental$type <- "Rental"
Q2Y15 <- rbind(rs_first_return, rs_first_rental)
Q4Y16 <- rbind(rs_last_return, rs_last_rental)
pal <- colorFactor(c("navy", "red"), domain = c("Return", "Rental"))
# Purple: popular for both return and rental
# Red: only for rental
# Blue: only for return
# Size: total frequency within the quarter
# for Q2 of 2015
leaflet(Q2Y15) %>%
  addTiles() %>%
  addProviderTiles('CartoDB.Positron') %>%
  setView(lng=-75.163763, lat=39.952568, zoom = 13) %>%
  addCircleMarkers(
    radius = ~sqrt(count)/10,
    color = ~pal(type),
    stroke = FALSE, fillOpacity = 0.5, 
    popup = ~name
  )

# for Q4 of 2016
leaflet(Q4Y16) %>%
  addTiles() %>%
  addProviderTiles('CartoDB.Positron') %>%
  setView(lng=-75.163763, lat=39.952568, zoom = 13) %>%
  addCircleMarkers(
    radius = ~sqrt(count)/10,
    color = ~pal(type),
    stroke = FALSE, fillOpacity = 0.5, 
    popup = ~name
  )


In the above two interactive maps, the color purple represents popular rental and return stations, red for rental, and blue for return. The size of the dot denotes the total frequency of rides within the quarter (top: Q2 of 2015, bottom: bottom: Q4 of 2016). Crudely in line with our hypothesis, most of the most popular stations (by dot size) are concentrated in the epicenter of Philadelphia, which is roughly between Rittenhouse Square and Chinatown. We also see that PMOA is off in the distance to the northeast, but was a popular rental and return station in Q2 of 2015. This also corresponds to our theory that summer made this a popular hub for bike users. In Q4 of 2016, the shift in clustering of popular stations revolves around the two shores of Schuylkill River. This may corroborate our observation that people use it as a means to cross the river for routine commutes (e.g. between University City/Powelton Village and Rittenhouse Square) that are out of walking range but close enough for a bike ride. This is purely speculative but makes for an interesting idea to further investigate. We will touch on this a bit more in the next section.

Daily traffic

We put the theory of using Indego as a form of commute to the test, using daily traffic as a proxy for measurements. We picked Q2 of 2016, which is the quarter with the second most bike rides. We ideally would have liked to chosen Q3 of 2016 (most recent summer quarter), which is the most popular quarter, but this time period overlaps with summer vacation for many students. Since we did not want to ignore this particular demographic when analyzing the use of bike trips as a form of commute, we thus settled on Q2 of 2016. We considered average hourly uses of Indego bikes per day for every day of Q2 of 2016. We then plotted this on a 24-hour time-scale, which is shown below. As illustrated by the plot, there are drastic differences between weekday and weekend usage across any standard 24-hour window. On weekdays, peak periods of bike trips were around 8:00 AM and 5:00 PM, which correspond to the time frames that most individuals commute respectively to and from work and school. On weekends, however, peak usage occured during the afternoon. Moreover, more bike trips were observed on weekdays for almost any given time interval than that of weekends. Thus, this may potentially demonstrate that Indego has become a means for everyday commute in Philadelphia.

# 2. Peak Periods (group by hour, weekdays/weekends, season)
# 2.1 Weekdays/weekends histogram of start (rental) hours for all stations in 2016Q2
library(lubridate)
Q2Y16 <- df[df$year==2016 & df$month %in% c("04","05","06"), ]
Q2Y16$day_type <- chron::is.weekend(Q2Y16$date)
Q2Y16$day_type <- ifelse(Q2Y16$day_type == 1, "Weekends", "Weekdays")
n_wknds <- length(unique(Q2Y16[Q2Y16$day_type=="Weekends",14]))
n_wkdys <- length(unique(Q2Y16[Q2Y16$day_type=="Weekdays",14]))
wknds_peak <- Q2Y16[Q2Y16$day_type=="Weekends",] %>% group_by(s_hour,day_type) %>% summarize(rental_freq = n()/n_wknds)
wkdys_peak <- Q2Y16[Q2Y16$day_type=="Weekdays",] %>% group_by(s_hour,day_type) %>% summarize(rental_freq = n()/n_wkdys)
peak <- rbind(wknds_peak, wkdys_peak)
rm(wknds_peak, wkdys_peak)
peak$s_hour <- as.numeric(as.character(peak$s_hour))
foo1 <- c(0,4,8,12,16,20)
foo2 <- c("12am","4am","8am","12pm","4pm","8pm")
ggplot(peak, aes(s_hour, rental_freq)) +
  geom_line(aes(color=day_type)) +
  geom_point(size = 1.1, aes(color=day_type)) +
  labs(title="Peak Rental Hours 2016Q2",
       subtitle="Average Daily Rental Frequency by Hour") +
  scale_color_fivethirtyeight("") +
  theme_fivethirtyeight() +
  theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
  scale_x_continuous(breaks = foo1, labels = foo2)

We now extend our analysis to all quarterly data available, which is shown in the plot immediately below. Much of the same trends from Q2 of 2016 can be generalized to other quarters. Apart from a few data points, the number of bike trips on weekdays are almost always larger than that of weekends. However, we also observe the aforementioned seasonality of bike trips, in which more bike trips are observed in the summer than in the winter months. This is especially true for trips on weekend (e.g. usage on weekends in Sept of 2015 is by far the largest). Therefore, use of Indego as leisure during summer weekends may be the causal variable of the peaks we observe.

# 2.2 Month aggregation of results
quarter_aggr <- df
#quarter_aggr$quarter <- (as.numeric(as.character(quarter_aggr$month)) - 1) %/% 3 + 1
quarter_aggr$day_type <- chron::is.weekend(quarter_aggr$date)
quarter_aggr$day_type <- ifelse(quarter_aggr$day_type == 1, "Weekends", "Weekdays")
quarter_aggr <- quarter_aggr %>% select(year, month, date, day_type)
quarter_aggr <- quarter_aggr %>% group_by(date, year, month, day_type) %>% summarise(count=n())
quarter_aggr <- quarter_aggr %>% group_by(year, month, day_type) %>% summarise(average=mean(count))
quarter_aggr$no <- (0:(nrow(quarter_aggr)-1)) %/% 2 + 1
foo1 <- seq(1,21,by=3)
foo2 <- c("Apr 2015","Jul 2015","Oct 2015", "Jan 2016", "Apr 2016","Jul 2016","Oct 2016")
ggplot(quarter_aggr, aes(no, average)) +
  geom_line(aes(color=day_type)) +
  geom_point(size = 1.1, aes(color=day_type)) +
  labs(title="Monthly Rental Pattern",
       subtitle="Daily Average of Rental Frequencies") +
  scale_color_fivethirtyeight("") +
  theme_fivethirtyeight() +
  theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
  scale_x_continuous(breaks = foo1, labels = foo2) +
  theme(axis.text.x = element_text(angle = 30, hjust = 1))

It is important to note that one of the more glaring deficiencies of our raw dataset is the lack of historical data; we only have data from April 2015 onwards. It thus makes it more difficult to draw conclusive remakrs regarding yearly or even seasonal patterns that require data over longer time periods. Nonetheless, several important trends can still be observed over the time period for which the dataset is available.

For instance, let us take a look at monthly bike trips as determined from the number of average trips per day, shown below. Such an aggregation, as opposed to taking the monthly average as is, was done to smooth out daily fluctuations that can be considered noise as we make our time scale more granular. We observe two main themes. First, there seems to be particular months in which bike trips are more and less frequent. Across any given calendar year, there are more bike rides through the summer, ultimately peaking in September. Bike trips decline thereafter, hitting low points in December/January. This is intuitive as good weather is correlated with how willing and practical bike rides function as a means of transportation. Thus, with abysmal weather conditions in the winter, people are probably less willing to use Indego bikes. Second, we observe Indego’s popularity increase through time based on the number of bike trips in a given month as compared to the same month in a preceding year. In Sept 2016, usage was at an all-time high, with more than 2600 rides per day (cf. approximately 2250 rides in Sept 2016). In Dec 2016, bike rides were roughly a little higher than 1000 rides per day (cf. 600 in Dec 2015). It should be noted that confounding factors such as weather conditions in a given year (e.g. El Nino) and the insufficient historical data we have on-hand may somewhat render our results questionable. We, however, believe this is not the case since more ecofriendly means of transportation should theoretically become more popular due to growing environmental sentiments in the mass population.

# Monthly Bike Trips
df_count_by_month <- df %>% group_by(year, month) %>% summarize(count = n())
df_average_by_month <- df %>% group_by(year, month, day) %>% summarize(count = n())
df_average_by_month <- df_average_by_month %>% group_by(year, month) %>% summarize(avg = mean(count))
df_count_by_month$date <- zoo::as.yearmon(paste(df_count_by_month$year, df_count_by_month$month), "%Y %m")
df_count_by_month$average <- df_average_by_month$avg
df_count_by_month$no <- 1:nrow(df_count_by_month)
df_count_by_month <- df_count_by_month[,-c(1,2)]
rm(df_average_by_month)
df_count_by_month <- tidyr::gather(df_count_by_month, type, value, c(1,3))
foo1 <- c(0, 500, 1000, 1500, 2000, 2500, 3000)
foo2 <- c(3, 6, 9, 12, 15, 18, 21)
ggplot() +
  geom_line(aes(no, value, color="blue"),
            df_count_by_month[df_count_by_month$type=="average",]) +
  geom_point(size = 1.1) +
  ggtitle("Monthly Indego Bike Trips\n(Average per day)") +
  xlab("Month") +
  ylab("Count") +
  scale_color_fivethirtyeight("cyl") +
  theme_fivethirtyeight() +
  theme(axis.text.x = element_text(angle = 30, hjust = 1),legend.position="none") +
  scale_y_continuous(limits = c(0, 3000), breaks = foo1) +
  scale_x_continuous(breaks = foo2, labels = df_count_by_month$date[foo2]) +
  theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm"))

As an interesting tangent, we examine the most popular routes based on a daily average of trips betweeen all stations across available quarters. We see that the most popular routes frequented by most users are those between hubs (i.e. already popular stations). In the interactive map below, we plot the Euclidean distance of the top five most popular routes. For instance, one such route is that between University Station and 23rd and South (i.e. downtown). It is worth noting that most of these routes are either across the downtown area or across the Schuylkill River, which somewhat substantiates the assertion that popular routes are a means for commute.

######## Popular Routes ######## 
# Content
# Most Popular 20 Routes
# Single dots means rent from these stations and return to these stations as well
popular_route <- df %>% group_by(date, year, month, start_station_id, end_station_id) %>% summarise(count=n())
popular_route <- popular_route %>% group_by(start_station_id, end_station_id) %>% summarise(avg=mean(count)) %>% na.omit
popular_route <- popular_route[order(-popular_route$avg),][1:20,]
# Extract all points
loc <- c(popular_route$start_station_id, popular_route$end_station_id) %>% unique %>% as.integer
station_loc$id <- as.numeric(station_loc$id)
dot_loc <- data.frame(id=loc) %>% left_join(station_loc, by=c("id"="id")) %>% na.omit
l1 <- leaflet(dot_loc) %>%
  addTiles() %>%
  addProviderTiles('CartoDB.Positron') %>%
  setView(lng=-75.163763, lat=39.952568, zoom = 13) %>%
  addCircleMarkers(
    color = "navy",
    stroke = FALSE, fillOpacity = 0.5, 
    popup = ~name
  )
popu <- popular_route[popular_route$start_station_id!=popular_route$end_station_id,]
dot_loc2 <- dot_loc
colnames(popu)[1] <- "id"
popu$id <- as.numeric(popu$id)
popu <- popu %>% left_join(dot_loc2, by=c("id"="id")) %>% na.omit
colnames(popu) <- c("sid", "id", "avg", "s_lat", "s_long", "name", "date")
dot_loc2$id <- as.numeric(dot_loc2$id)
popu <- popu %>% left_join(dot_loc2, by=c("id"="id")) %>% na.omit
colnames(popu) <- c("sid", "id", "avg", "s_lat", "s_long", "s_name", "date", "e_lat", "e_long", "e_name", "date_e")
for(i in 1:nrow(popu)){
  l1 <- addPolylines(l1, lat = as.numeric(popu[i, c(4, 8)]), lng = as.numeric(popu[i, c(5, 9)]))
}
l1

Based on the above findings, we can say with a certain level of confidence that the volume of bike trips is an important indicator of Indego’s utility rate over different cross-sections of time. However, it does little to tell us how (not how much) Indego is being utilized. With this in mind, we motivate our next portion of analysis.

Since the inherent purpose of bike share networks are to mediate relatively short commutes, we want to examine the length of each trip. We surmise that shorter trips (0-30 minutes) should be the ideal range since anything beyond that is simply exercise and other transportation methods become increasingly more appealing. We consequently explore features related to the length of bike rides next.

Trip durations

In this section, we try to elucidate the more salient patterns pertaining to trip durations. From the two histograms below, which illustrate the durations of all trips in Q2 and Q4 of 2016 across weekdays and weekends, we observe that most trips taken were under 15 minutes. These two particular quarters were chosen since they are the latest quarters that also offers a contrast in seasonality. During weekdays for both quarters, shorter commutes (< 15 minutes) were the overwhelming majority of trip durations. They make up more than 70% of the total count. The same is true for weekends, but to a lesser extent. This may once again be an indicator that bikes used during weekdays are for commute (e.g. to and from work and school) so they are inherently shorter, whereas bikes are used for more recreational purposes on weekends so their time of use is naturally longer. The latter is corroborated by the fact that during summer weekends, the frequency of longer trips outweighed that of shorter trips.

If we ignore any differentiation of trip durations between weekdays and weekends, we observe much of the same trends. For Q4 of 2016, the latest quarterly data available to us, the most prevalent duration of bike rides lie between 5 and 10 minutes. In fact, the overwhelming majority of trips are less than 30 minutes. On the contrary, we see that long trips are actually outliers in our dataset. Trips between two and four hours only make up 0.9% of all rides in Q4 of 2016, and trips exceeding 4 hours make up 0.7% of the total.

# 1. General Pattern Visualization
# 1.1 Trip Duration Distributions (dimension of time)
# Weekdays/weekends in 2016Q2 and 2016Q4
library(ggthemes)
Q2Y16 <- df[df$year==2016 & df$month %in% c("04","05","06"), ]
Q2Y16$day_type <- chron::is.weekend(Q2Y16$date)
Q2Y16$day_type <- ifelse(Q2Y16$day_type == 1, "Weekends", "Weekdays")
Q4Y16 <- df[df$year==2016 & df$month %in% c("10","11","12"), ]
Q4Y16$day_type <- chron::is.weekend(Q4Y16$date)
Q4Y16$day_type <- ifelse(Q4Y16$day_type == 1, "Weekends", "Weekdays")
Q2Y16$duration_cut <- cut(as.numeric(Q2Y16$duration), c(0,5,10,15,20,30,60,120,240,1440))
Q4Y16$duration_cut <- cut(as.numeric(Q4Y16$duration), c(0,5,10,15,20,30,60,120,240,1440))
Q2Y16_dur <- Q2Y16 %>% group_by(day_type, duration_cut, date) %>% summarise(count=n())
Q2Y16_dur<- Q2Y16_dur %>% group_by(day_type, duration_cut) %>% summarise(average=mean(count))
Q2Y16_dur$Quarter <- "2016Q2"
Q4Y16_dur <- Q4Y16 %>% group_by(day_type, duration_cut, date) %>% summarise(count=n())
Q4Y16_dur<- Q4Y16_dur %>% group_by(day_type, duration_cut) %>% summarise(average=mean(count))
Q4Y16_dur$Quarter <- "2016Q4"
dur_cut <- rbind(Q2Y16_dur, Q4Y16_dur)
rm(Q2Y16_dur, Q4Y16_dur, Q2Y16, Q4Y16)
foo <- c("<5min", "5-10min", "10-15min", "15-20min", "20-30min", "30-60min", "1-2hr", "2-4hr", ">4hr")
ggplot(dur_cut, aes(duration_cut, average)) +
  geom_bar(aes(fill=Quarter), stat = 'identity', position = position_dodge()) +
  facet_grid(. ~ day_type, scales = "free") +
  labs(title="Daily Trip Duration Histogram",
       subtitle="For 2016Q2 and 2016Q4",
       xlab="") +
  scale_color_fivethirtyeight(" ") +
  theme_fivethirtyeight() +
  theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
  scale_x_discrete(labels = foo) +
  theme(axis.text.x = element_text(angle = 30, hjust = 1)) + 
  scale_fill_manual(values = c("#0072B2", "#CC79A7"))

We next examine the median rental duration for each station, particularly for Q2 to Q4 of 2016. We chose this range since it was the only range of contiuous quarterly data that had complete station entries for trip durations. In the interactive map below, larger circles correspond to larger values with respect to trip durations. The color scheme remains the same as previously mentioned. Therefore, a larger red circle implies that a bike user rented from a particular station and rode their bike for a longer duration. We see that larger red circles correspond to stations in the outskirts of Philadelphia. Therefore, they either used the bike for a longer commute or they used for leisurely purposes. For instance, one of the larger red circles denotes PMOA, which we previously mentioned as a popular station during the summer and weekends. However, it is impossible to differentiate between the two based on our dataset alone. It is however intriguing in nature.

# 1.2 Median Rental Duration for Each Station (2016 Q2-Q4) (dimension of space)
# choose 2016 Q2-Q4 since all stations have complete data in this period
station_ana2 <- df[df$year==2016 & df$month %in% c("04","05","06","07","08","09","10","11","12"), ]
median_stat <- station_ana2 %>% group_by(start_station_id) %>% summarise(med=median(duration))
# Plot in a map, dot size = median rental time
colnames(median_stat)[1] <- "id"
station_loc$id <- as.numeric(station_loc$id)
median_stat$id <- as.numeric(median_stat$id)
median_stat <- median_stat %>% left_join(station_loc, by=c("id"="id")) %>% na.omit
content <- paste("<b>", as.character(median_stat$name), "</b><br>",
                 "Median Rental Time: ", median_stat$med, "minutes", "<br>")
median_stat$chosen <- median_stat$id %in% c("3020", "3050", "3124")
median_stat$chosen <- ifelse(median_stat$chosen == 1, "Chosen", "Unchosen")
pal <- colorFactor(c("navy", "red"), domain = c("Chosen", "Unchosen"))
leaflet(median_stat) %>%
  addTiles() %>%
  addProviderTiles('CartoDB.Positron') %>%
  setView(lng=-75.163763, lat=39.952568, zoom = 13) %>%
  addCircleMarkers(
    radius = ~med/2,
    color = ~pal(chosen),
    stroke = FALSE, fillOpacity = 0.5, 
    popup = ~content
  )


The dots colored in navy are those that we chose to further analyze, which we present here. They correspond to the following stations:

  • 3020: University City Station
  • 3050: 9th & Arch (downtown)
  • 3124: Race Street Pier (away from university and downtown)
# 2. Station-level rental time
# Select stations
# Three locations that could have totally different travel patterns:
# 3020: University City Station, global avg duration 17.8
# 3050: 9th & Arch, (downtown), global avg duration 22.4
# 3124: Race Street Pier, (away from university and downtown), global avg duration 43.4
# 2.1 Over hour of a day
station_ana <- df[df$year==2016 & df$month %in% c("04","05","06","07","08","09","10","11","12"), ]
foo <- station_ana %>% group_by(start_station_id) %>% summarise(average=mean(duration)) # select stattions
station_ana <- station_ana[station_ana$duration<240,] # !! Truncate duration at 4 hrs to remove outliers
station_ana <- station_ana[station_ana$start_station_id %in% c("3020", "3050", "3124"),]
station_ana$day_type <- chron::is.weekend(station_ana$date)
station_ana$day_type <- ifelse(station_ana$day_type == 1, "Weekends", "Weekdays")
foo <- station_ana %>% group_by(start_station_id) %>% summarise(count=n()) # check total trips
station_ana <- station_ana %>% group_by(start_station_id, day_type, s_hour) %>% summarise(avg_dur=mean(duration))
foo <- data.frame(start_station_id = rep(c("3020", "3050", "3124"),each=48),
                  day_type = rep(rep(c("Weekdays", "Weekends"),each=24),3),
                  s_hour = rep(0:23,6))
station_ana$s_hour <- as.numeric(as.character(station_ana$s_hour))
station_ana$start_station_id <- as.character(station_ana$start_station_id)
station_ana$day_type <- as.character(station_ana$day_type)
station_ana <- station_ana %>% right_join(foo, by=c("start_station_id"="start_station_id",
                                                    "day_type"="day_type",
                                                    "s_hour"="s_hour"))
station_ana[is.na(station_ana)] <- 0
foo1 <- c(0,4,8,12,16,20)
foo2 <- c("12am","4am","8am","12pm","4pm","8pm")
ggplot(station_ana[station_ana$start_station_id=="3020",], aes(s_hour, avg_dur)) +
  geom_line(aes(color=day_type)) +
  geom_point(size = 1.1, aes(color=day_type)) +
  labs(title="Average Trip Duration 2016 Q2-Q4",
       subtitle="At (3020) University City Station, Total Trips: 11920") +
  scale_color_fivethirtyeight("") +
  theme_fivethirtyeight() +
  theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
  scale_x_continuous(breaks = foo1, labels = foo2)

ggplot(station_ana[station_ana$start_station_id=="3050",], aes(s_hour, avg_dur)) +
  geom_line(aes(color=day_type)) +
  geom_point(size = 1.1, aes(color=day_type)) +
  labs(title="Average Trip Duration 2016 Q2-Q4",
       subtitle="At (3050) 9th & Arch, Total Trips: 6121") +
  scale_color_fivethirtyeight("") +
  theme_fivethirtyeight() +
  theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
  scale_x_continuous(breaks = foo1, labels = foo2)

ggplot(station_ana[station_ana$start_station_id=="3124",], aes(s_hour, avg_dur)) +
  geom_line(aes(color=day_type)) +
  geom_point(size = 1.1, aes(color=day_type)) +
  labs(title="Average Trip Duration 2016 Q2-Q4",
       subtitle="At (3124) Race Street Pier, Total Trips: 3729") +
  scale_color_fivethirtyeight("") +
  theme_fivethirtyeight() +
  theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
  scale_x_continuous(breaks = foo1, labels = foo2)

In the first plot (University City Station), we consdier the station close to a university. We see that on average, longer trips occur on the weekend. In fact, none of the trips on weekdays exceed 25 minutes. We also observe that in both cases, longer trips tend to occur during the day, whereas shorter trips occur in the early morning and towards night time. In the second plot delineating the downtown station (9th & Arch), we see much of the same trend as that of the previous case. However, the discrepancy between trip durations across a 24-hour window on the weekends and the weekdays is much more pronounced. In our last plot above (Race Street Pier), we consider a station far from a university or downtown, which we can consider to be “more rural.” Here, trips on weekdays and weekends mirror one another, showing they are closely related. Moreover, trips from this more rural station are all much longer, most exceeding 20 minutes irrespective of time intervals during the week. Therefore, it would seem that different stations based on their geographical location serve a different purpose for bike riders. Stations in more densely populated areas tend to favor shorter rides, whereas longer rides are more prevalent in more remote locations. It is important to note that the magnitude of number of trips may introduce some sort of counfounder on our insights (e.g. data imbalance), but we ignore this here, since it is difficult to address this issue given the limited historical data we have. This is a problem that would be solved naturally as more data becomes available.

# 2.2 Over month of a year
station_ana2 <- df[df$year==2016 & df$month %in% c("04","05","06","07","08","09","10","11","12"), ]
station_ana2 <- station_ana2[station_ana2$start_station_id %in% c("3020", "3050", "3124"),]
station_ana2 <- station_ana2[station_ana2$duration<240,] # !! Truncate duration at 4 hrs to remove outliers
station_ana2 <- station_ana2 %>% group_by(start_station_id, month) %>% summarise(med=median(duration))
colnames(station_ana2)[1] <- "id"
station_ana2$id <- as.numeric(station_ana2$id)
station_ana2 <- station_ana2 %>% left_join(station_loc, by=c("id"="id"))
station_ana2$month <- as.numeric(as.character(station_ana2$month))
foo1 <- 4:12
foo2 <- c("Apr","May","Jun","Jul","Aug","Sept","Oct","Nov","Dec")
ggplot(station_ana2, aes(month, med)) +
  geom_line(aes(color=name)) +
  geom_point(size = 1.1, aes(color=name)) +
  labs(title="Median of Trip Duration",
       subtitle="Over Months (2016 Q2-Q4)") +
  scale_color_fivethirtyeight("") +
  theme_fivethirtyeight() +
  theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
  scale_x_continuous(breaks = foo1, labels = foo2)

We realize it may be difficult to examine the trends of all three plots concurrently so we thus plot the median trip duration of all three stations in one single plot, which is shown above. It is interesting to note that over time, shorter rides become the prevalent theme. This is true for all three stations. However, the more rural station is still characterized by longer trips relative to the other two stations that are close to a university and city center.

Thus far, we have examined how and how much Indigo is utilized. However, we still do not know how users select to utilize Indego. For instance, which methods of payment are more common? Is there a change in preference over time? We seek to answer these questions in the next and final section of our analysis.

User profiles

In this section, we examine the attributes related to Indego bike users. Since the dataset provides only two dimensions regarding this aspect, we use the conventional mosaic plot to examine the proportions of different combinations of Indego users. These two features are passholder types (Indego30, IndegoFlex, and walkup) and type of trips (one-way, roundtrip).

Here is some more relevant information regarding the different passholder types:

  • Indego30: $15/month, unlimited one hour trips
  • IndegoFlex: $10/year base fee, and $4/hour
  • Walk-up: $8/hour
# 1.1 Mosaic plot of overall data
all_data_mosaic <- df %>% group_by(passholder_type, trip_route_category) %>% summarize(count = n())
colnames(all_data_mosaic) <- c("Pass", "Trip", "count")
library(vcd)
vcd::mosaic(xtabs(count ~ Trip + Pass, data = all_data_mosaic),
            direction = c("v", "h"),
            gp = gpar(fill = c("light green", "light blue", "light yellow")),
            spacing = spacing_highlighting,
            labeling = labeling_border(rot_labels = c(0,90,0,0)))

For all available quarters, we can see that the majority of users drastically favor one-way over round trips, and Indego30 passes over walkup payment plans. IndegoFlex is the least popular option regardless of route type. In fact, the combination of one-way trips with Indego30 is overwhelming characteristic, accounting for >50% of all users, followed by one-way with walk-ups. However, this is a extremely rough representation of user profiles since we ignore how user profiles vary over time. We examine this next.

Let us examine the most popular option (Indego30 x one-way), as shown below. We see that this option has become more popular at a steady rate, in which more than 75% of users choose this option in the lastest quarter.

# 1.2 Type of passholders, frequency of round-trips (for month)
# for most popular type of trip route and pass type
# 2. Tendency of Moving to Most Popular Category
# Plot of the percentage of (passholder_type, trip_route_category) over quarters
# For most popular category
perc_category <- df %>% group_by(passholder_type, trip_route_category, year, month) %>% summarize(count = n())
perc_category$quarter <- (as.numeric(as.character(perc_category$month)) - 1) %/% 3 + 1
perc_category <- perc_category %>% group_by(year, quarter, passholder_type, trip_route_category) %>% summarise(count_qt=sum(count)) 
perc_category$type <- paste(perc_category$trip_route_category, "x", perc_category$passholder_type)
perc_category <- perc_category %>% group_by(year, quarter) %>% mutate(total=sum(count_qt))
perc_category$perc <- round(100*(perc_category$count_qt / perc_category$total),2)
perc_category <- perc_category[,-c(3:5,7)]
perc_category$no <- rep(1:7,each=6)
foo1 <- 1:7
foo2 <- c("2015Q2","2015Q3","2015Q4", "2016Q1", "2016Q2","2016Q3","2016Q4")
foo3 <- c("One Way x Indego30")
tmp1 <- perc_category[perc_category$type %in% foo3,]
ggplot(tmp1, aes(no, perc)) +
  geom_line(aes(colour=type)) +
  geom_point(aes(colour=type),size = 1.1) +
  labs(title="Percentage Change Over Quarters",
       subtitle="For Most Popular Category\nOne Way x Indego30") +
  scale_color_fivethirtyeight("") +
  theme_fivethirtyeight() +
  theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
  scale_x_continuous(breaks = foo1, labels = foo2) +
  scale_y_continuous(limits = c(0, 100))

We see from the previous mosaic plot that round trips are not popular on average. We want to test whether there does indeed exist a decline in popularity over time. We see from the percentage change over quarters shown below that regardless of pass types, round trips make up an increasingly smaller fraction of the user demographic. We also postulate that the already unpopular combination of round trips and Indego Flex would make up an increasingly smaller proportion as well since they are more frequent travellers that would gradually move towards cheaper plans. This however is speculative and is contingent on a robust, large enough longitudinal dataset, which we do not have. Nontheless, this problem, albeit interesting, is beyond the scope of our analysis.

# For roundtrips
foo4 <- c("Round Trip x IndegoFlex", "Round Trip x Indego30", "Round Trip x Walk-up")
tmp2 <- perc_category[perc_category$type %in% foo4,]
ggplot(tmp2, aes(no, perc)) +
  geom_line(aes(colour=type)) +
  geom_point(aes(colour=type),size = 1.1) +
  labs(title="Percentage Change Over Quarters",
       subtitle="Over Categories") +
  scale_color_fivethirtyeight("") +
  theme_fivethirtyeight() +
  theme(plot.margin = unit(c(0.5,0.5,0.5,0.5), "cm")) +
  scale_x_continuous(breaks = foo1, labels = foo2) +
  scale_y_continuous(limits = c(0, 7.15))

Based on the above analysis on user profiles, we see that as bike sharing becomes more popular over time in Philadelphia, more users opt for more cost-efficient payment plans, which is by far Indego30. The rise of one-way trips also conjecturally demonstrate that users are using Indego more frequently as a means of transportation over short distances or time periods. Obviously, we ignore many important factors related to user profiles. For instance, is there a preference for those renting from particular stations or during particular months? This is a natural extension of our analysis that we would like to further explore time permitting. However, we believe that our current set of analysis on user profiles presented here serves to provide an overarching representation of user profiles.

Conclusion

In this project, we examined data related to Philadelphia’s bike share program, Indego. We examined all available quarterly data (Q2 of 2015 to Q4 of 2016) to identify noteworthy patterns and trends related to the various ways that Indego is utilized, mainly pertaining to station usage, daily traffic, trip durations, and user profiles. We found that popular rental and return stations are concentrated around locations that are densely populated (e.g. in close proximity to city centers or university campuses). We also note that seasonality affects the usage rate of certain Indego stations. Using daily traffic as a proxy to determine whether or not Indego is used as a means of commute, we also found an increase in frequency of bike trips on weekdays that conveniently overlaps with time periods when most people commute to and from work or school. Moreover, we observe a higher volume of bike trips during the weekday, all of which conjecturally demostrate the vercaity of our aforementioned claim.

We yet again observe trends of seasonality in daily traffic, in which volume during the summer far exceeds that of winter months. We also examined trip durations, and found that shorter trips (0-15 minutes) are the norm, but this is less evident for weekends than weekdays. Finally, we evaluated the proportions of Indego’s user profiles and found that most users prefer monthly payments and one-way trips. We also found that over time, more individuals are opting for yearly memeberships, which may speak to the fact that Indego is becoming a more popular means of short-range commute in Philadelphia.

It is worth mentioning that our analyses is extremely broad in nature. We would have liked to further explore certain trends and interaction effects. For instance, how does certain user profiles and stations correlate with each other? Are there any neighborhoods that have extremely high volumes of traffic? How does this vary monthly, quarterly, or even yearly? We would have liked to compared results across similar cities, and how they compare and constrast (e.g. with NYC’s citibike). Time permitting, these are the more imminent avenues we would have liked to explore.

Another caveat that should be brought to light is that the lack of historical data also makes it difficult to draw robust conclusions on year-to-year variability or even corroborate the significance of seasonal fluctuations. This problem obviously becomes less relevant as more quarterly data becomes available, but is a major obstacle in our current analysis for long-term time frames.

Nonetheless, with the data available, we believe that the exploratory data analysis and visualizations that we provide here serve as a good first-order approximation of salient trends related to Indego and the ways it is being effectively utilized. We hope this serves as a pragmatic catalyst for other readers interested in the Indego dataset, or using the insights and techinques provided here to explore other similar datasets.

Appendix: Shiny app

We also created a Shiny application for real-time status updates for all active Indego stations. This is purely supplemental, and we provide snapshots here, and instructions to run our application in our Github repo linked above. You can follow the instructions on the website to run it on a local machine.

LS0tCnRpdGxlOiAiU1RBVCA1NzAyOiBGaW5hbCBQcm9qZWN0IgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyBJbnRyb2R1Y3Rpb24KCiMjIyMgTW90aXZhdGlvbgogCkluZGVnbyBpcyB0aGUgZGUgZmFjdG8gYmlrZSBzaGFyaW5nIG5ldHdvcmsgaW4gUGhpbGFkZWxwaGlhLiBJdCBoYXMgYmVjb21lIGEgcG9wdWxhciwgbW9yZSBlY28tZnJpZW5kbHkgYWx0ZXJuYXRpdmUgZm9yIHNob3J0IHRyaXBzIGFjcm9zcyB0aGUgY2l0eSBzaW5jZSBsYXVuY2hpbmcgaW4gbGF0ZSBBcHJpbCBvZiAyMDE1LiBIb3dldmVyLCBsaXR0bGUgaGFzIGJlZW4gZG9uZSB0byBhbmFseXplIHRoZSBwdWJsaWNseSBhdmFpbGFibGUgZGF0YSBhc3NvY2lhdGVkIHdpdGggSW5kZWdvLiBUaHVzLCB3ZSB0aGluayBpdCB3b3VsZCBiZSBiZW5lZmljaWFsIHRvIHRoZSBwdWJsaWMgaWYgd2UgcXVhbnRpZnkgYW5kIGFzc2VzcyBzZXZlcmFsIHBlcnRpbmVudCBmZWF0dXJlcyBhbmQgcGF0dGVybnMgcmVsYXRlZCB0byBJbmRlZ28sIGdlbmVyYXRpbmcgaW5zaWdodHMgb24gaG93IHRoZSBiaWtlIHNoYXJpbmcgbmV0d29yayBpcyB1dGlsaXplZCBhY3Jvc3MgdGhlIGNpdHkuIE1vcmUgc3BlY2lmaWNhbGx5LCB3ZSB3YW50IHRvIG1vcmUgY2xvc2VseSBzdHVkeSBhbmQgYW5zd2VyIHF1ZXN0aW9ucyByZWxhdGVkIHRvIHRoZSBmb2xsb3dpbmcgZm91ciB0aGVtZXM6CgoqIFN0YXRpb24gdXNhZ2UgKHVzYWdlIHJhdGUsIHBlYWsgcGVyaW9kcywgcG9wdWxhciByZW50YWwvcmV0dXJuIHN0YXRpb25zLCBldGMuKSAKKiBEYWlseSB0cmFmZmljIChwb3B1bGFyIHJvdXRlcywgdm9sdW1lIGFjcm9zcyBkaWZmZXJlbnQgc2VjdGlvbnMsIHRyYXZlbCBwYXR0ZXJucywgZXRjLikKKiBUcmlwIGR1cmF0aW9ucyAobGVuZ3RoIG9mIHRyaXBzLCBtZWRpYW4gcmVudGFsL3JldHVybiB0aW1lLCBldGMuKQoqIFVzZXIgcHJvZmlsZXMgKHR5cGUgb2YgcGFzc2hvbGRlcnMsIGZyZXF1ZW5jeSBvZiByb3VuZC10cmlwcywgc2Vhc29uYWxpdHksIGV0Yy4pCgpFYWNoIG9mIHRoZSBhYm92ZSBmb3VyIGZlYXR1cmVzIHdpbGwgYmUgZXhwbGFpbmVkIGluIGZpbmVyIGRldGFpbHMgaW4gc3Vic2VxdWVudCBzZWN0aW9ucy4gVG8gcmVhbGl6ZSB0aGlzIG9iamVjdGl2ZSwgd2UgaGF2ZSBjcmVhdGVkIHN0YXRpYyBwbG90cyAoc2hvd24gaW4gdGhpcyBub3RlYm9vaykgdG8gZGVzY3JpYmUgc2FsaWVudCB0aGVtZXMgYXMgd2VsbCBhcyBhIFNoaW55IGFwcCAoW0dpdGh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL21hc29uc3VuL2dyNTcwMi1wcm9qKSksIHdoaWNoIGFsc28gY29udGFpbnMgcmVhbC10aW1lIHN0YXR1cyB1cGRhdGVzIG9mIGFsbCBJbmRlZ28gc3RhdGlvbnMgYWNyb3NzIFBoaWxhZGVscGhpYS4gV2Ugd2lsbCBhbHNvIHByb3ZpZGUgaW5zdHJ1Y3Rpb25zIG9uIHJ1bm5uaW5nIG91ciBhcHAgZnVydGhlciBkb3duIGluIG91ciByZXBvcnQuCgpPdXIgb2JqZWN0aXZlIHN0YXJ0aW5nIG91dCB3YXMgdG8gZXhhbWluZSBiaWtlIHNoYXJlIHByb2dyYW1zIGluIHRoZSBtb3N0IGRlbnNlbHkgcG9wdWxhdGVkIGNpdGllcyBhY3Jvc3MgdGhlIGVhc3Rlcm4gVW5pdGVkIFN0YXRlcywgbWFpbmx5IHZpYSBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIGFuZCB2aXN1YWxpemF0aW9uIHRlY2hpbnF1ZXMuIFdlIGluaXRpYWxseSBjb25zaWRlcmVkIE5ZQydzIENpdGliaWtlIGFzIGEgcHJvbWlzaW5nIGNhbmRpZGF0ZSwgYnV0IGl0IGhhcyBhbHJlYWR5IGJlZW4gZXh0ZW5zaXZlbHkgYW5hbHl6ZWQgKHNlZSBbVG9kZCBTY2huZWlkZXIncyBibG9ncG9zdF0oaHR0cDovL3RvZGR3c2NobmVpZGVyLmNvbS9wb3N0cy9hLXRhbGUtb2YtdHdlbnR5LXR3by1taWxsaW9uLWNpdGktYmlrZXMtYW5hbHl6aW5nLXRoZS1ueWMtYmlrZS1zaGFyZS1zeXN0ZW0vKSBmb3IgYW4gZXhjZWxsZW50IGV4YW1wbGUpLiBUaGUgbWFyZ2luYWwgcmV0dXJucyBvZiBhbmFseXppbmcgc3VjaCBhIGRhdGFzZXQgYXJlIHRodXMgbWluaW1hbCwgaWYgbm90LCBuaWwuIEFzIGEgcmVzdWx0LCB3ZSBkZWNpZGVkIG9uIFBoaWxhZGVscGhpYSwgcHJpbWFybGl5IGZvciB0d28gcmVhc29uczogaXQgaXMgdGhlIHNlY29uZCBsYXJnZXN0IGNpdHkgc2F0aXNmeWluZyBvdXIgcHJlZGVmaW5lZCBjcml0ZXJpYSAoYWZ0ZXIgTllDKSwgYW5kIGFsc28gaGFzIGEgcmVsYXRpdmVseSBuZXcgYmlrZSBzaGFyZSBwcm9ncmFtLiBUaHVzLCB3ZSB0aG91Z2h0IHRoZSBJbmRlZ28gZGF0YXNldCB3b3VsZCBiZSBhbiBpZGVhbCBjaG9pY2UuIEl0IHdvdWxkIGFsc28gYmUgaW50ZXJlc3RpbmcgdG8gdXNlIG91ciBkYXRhc2V0IHRvIGNvbXBhcmUgYW5kIGNvbnRyYXN0IE5ldyBZb3JrIGFuZCBQaGlsYWRlbHBoaWEncyBiaWtlIHNoYXJlIHByb2dyYW1zLgoKIyMjIyBEYXRhIFNvdXJjZQoKVGhlIGRhdGFzZXQgaXMgcHVibGljbHkgYXZhaWxhYmxlIG9uIFtJbmRlZ28ncyB3ZWJzaXRlXShodHRwczovL3d3dy5yaWRlaW5kZWdvLmNvbS9hYm91dC9kYXRhLykuIEl0IGNhbiBiZSBmb3VuZCBieSBjbGlja2luZyB0aGUgPGk+QWJvdXQ8L2k+IHBhbmVsIGFuZCB0aGVuIHNlbGVjdGluZyA8aT5EYXRhPC9pPi4gQWxsIHF1YXJ0ZXJseSBkYXRhIGFyZSBsaXN0ZWQgdW5kZXIgdGhlIHNlY3Rpb24gPGk+VGhlIERhdGE8L2k+LCBpbiB3aGljaCBlYWNoIHN0YXRpb24ncyBpbmZvcm1hdGlvbiBpcyBhdmFpbGFibGUgdGhyb3VnaCB0aGUgPGk+U3RhdGlvbiBUYWJsZTwvaT4gaHlwZXJsaW5rLiBJbiBhZGRpdGlvbiwgcmVhbC10aW1lIGRhdGEgdXNlZCBpbiBvdXIgU2hpbnkgYXBwIGlzIGF2YWlsYWJsZSB1bmRlciB0aGUgc2VjdGlvbiA8aT5JbmRlZ28gU3RhdGlvbiBTdGF0dXM8L2k+IHZpYSB0aGUgR2VvSlNPTiBoeXBlcmxpbmsuCgojIyBUZWFtCgoxLiBLZWppYSBTaGkgKGtzMzQwMyk6IGRhdGEgcHJvY2Vzc2luZywgZXhwbG9yYXRvcnkgZGF0YSBhbmFseXNpcywgdmlzdWFsaXphdGlvbiAoc3RhdGljIGFuZCBTaGlueSBwbG90cyksIHdyaXRpbmcvZWRpdGluZyByZXBvcnQuCjIuIE1hc29uIFN1biAoenMyMzIxKTogZGF0YSBwcm9jZXNzaW5nLCBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzLCBkZXZlbG9waW5nIGZyb250L2JhY2tlbmQgb2YgU2hpbnkgYXBwLCB3cml0aW5nL2VkaXRpbmcgcmVwb3J0LgozLiBIdW5nLVNoaSBMaW4gKGhsMjk5Nyk6IHByb29mcmVhZC4KCiMjIEFuYWx5c2lzIG9mIERhdGEgUXVhbGl0eQoKIyMjIyBRdWFydGVybHkgZGF0YQpGb3IgcXVhcnRlcmx5IGRhdGEsIHdlIGhhZCBzZXZlbiBmaWxlcyBpbiB0b3RhbCwgZWFjaCByZXByZXNlbnRpbmcgZGF0YSBiZXR3ZWVuIFEyIG9mIDIwMTUgYW5kIFE0IG9mIDIwMTYsIGluY2x1c2l2ZWx5LiBFYWNoIGNvbnRhaW5lZCB0aGUgc2FtZSBjb2x1bW5zOgoKKiB0cmlwX2lkICh1bmlxdWUgaWRlbnRpZmllciBvZiBhIGJpa2UgdHJpcCkKKiBkdXJhdGlvbiAobGVuZ3RoIG9mIHRyaXAgaW4gc2Vjb25kcyBvciBtaW51dGVzIGNhbGN1bGF0ZWQgYnkgY2hlY2staW4gYW5kIGNoZWNrLW91dCB0aW1lKQoqIHN0YXJ0X3RpbWUgKGNsb2NrIHN0YXJ0IHRpbWUgYW5kIGNhbGVuZGFyIGRhdGUpCiogZW5kX3RpbWUgKGNsb2NrIGVuZCB0aW1lIGFuZCBjYWxlbmFyIGRhdGUpCiogc3RhcnRfc3RhdGlvbl9pZCAodW5pcXVlIGlkZW50aWZpZXIgb2Ygc3RhcnRpbmcgc3RhdGlvbikKKiBzdGFydF9sYXQgKGxhdGl0dWRlIG9mIHN0YXJ0aW5nIHN0YXRpb24pCiogc3RhcnRfbG9uIChsb25naXR1ZGUgb2Ygc3RhcnRpbmcgc3RhdGlvbikKKiBlbmRfc3RhdGlvbl9pZCAodW5pcXVlIGlkZW50aWZpZXIgb2YgZW5kIHN0YXRpb24pCiogZW5kX2xhdCAobGF0aXR1ZGUgb2YgZW5kIHN0YXRpb24pCiogZW5kX2xvbiAobG9uZ2l0dWRlIG9mIGVuZCBzdGF0aW9uKQoqIGJpa2VfaWQgKHVuaXF1ZSBpZGVudGlmaWVyIG9mIHRoZSBiaWtlIHVzZWQgZHVyaW5nIHRoZSB0cmlwKQoqIHBsYW5fZHVyYXRpb24gKHR5cGUgb2YgcGFzcyBieSBudW1iZXIgb2YgZGF5cywgZS5nLiB0aGlydHktZGF5IHBhc3MgaXMgc2hvd24gYXMgMzApCiogdHJpcF9yb3V0ZV9jYXRlZ29yeSAocm91bmQgdHJpcCBvciBvbmUtd2F5KQoqIHBhc3Nob2xkZXJfdHlwZSAod2Fsa3VwLCBJbmRlZ29GbGV4LCBvciBJbmRlZ28zMCkKCldlIGNvbWJpbmVkIHRoZXNlIGludG8gb25lIGRhdGEgZnJhbWUgKGNhbGxlZCA8aT5kZjwvaT4pIHVzaW5nIHRoZSBjb2RlIGFzIGZvbGxvd3MuIFRoZSBkZXNjcmlwdGlvbiBvZiBvdXIgcHJlcHJvY2Vzc2luZyBtZXRob2RzIGFyZSBlbWJlZGRlZCBhcyBjb21tZW50cyBpbiB0aGUgY29kZSBjaHVuayBiZWxvdy4gUGxlYXNlIHNlZSB0aGVtIGZvciBtb3JlIGRldGFpbGVkIGV4cGxhbmF0aW9ucy4KCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKIyMjIHJlYWQgaW4gcXVhcnRlcmx5IGRhdGEgIyMjClkxNlE0IDwtIHJlYWQuY3N2KCJkYXRhL0luZGVnb19Ucmlwc18yMDE2UTQuY3N2IiwgaGVhZGVyPVQpClkxNlEzIDwtIHJlYWQuY3N2KCJkYXRhL0luZGVnb19Ucmlwc18yMDE2UTMuY3N2IiwgaGVhZGVyPVQpClkxNlEyIDwtIHJlYWQuY3N2KCJkYXRhL0luZGVnb19Ucmlwc18yMDE2UTIuY3N2IiwgaGVhZGVyPVQpClkxNlExIDwtIHJlYWQuY3N2KCJkYXRhL0luZGVnb19Ucmlwc18yMDE2UTEuY3N2IiwgaGVhZGVyPVQpClkxNVE0IDwtIHJlYWQuY3N2KCJkYXRhL0luZGVnb19Ucmlwc18yMDE1UTQuY3N2IiwgaGVhZGVyPVQpClkxNVEzIDwtIHJlYWQuY3N2KCJkYXRhL0luZGVnb19Ucmlwc18yMDE1UTMuY3N2IiwgaGVhZGVyPVQpClkxNVEyIDwtIHJlYWQuY3N2KCJkYXRhL0luZGVnb19Ucmlwc18yMDE1UTIuY3N2IiwgaGVhZGVyPVQpCmluZGVnb19zdGF0aW9ucyA8LSByZWFkLmNzdigiZGF0YS9pbmRlZ29fc3RhdGlvbnMuY3N2IiwgaGVhZGVyPVQpCiMjIyBwcmVwcm9jZXNzaW5nICMjIwojIHNlbGVjdCBjdXJyZW50bHkgYWN0aXZlIHN0YXRpb25zIGZyb20gbW9zdCByZWNlbnQgcXVhcnRlcmx5IGRhdGFzZXQKc3RhdGlvbl9sb2MgPC0gWTE2UTQgJT4lIGRwbHlyOjpzZWxlY3Qoc3RhcnRfc3RhdGlvbl9pZCwgc3RhcnRfbGF0LCBzdGFydF9sb24pICU+JSB1bmlxdWUKIyBjcmVhdGUgYSBkYXRhZnJhbWUgdGhhdCBpbmNsdWRlcyB0aGUgcmVsZXZhbnQgY29sdW1ucyBmb3IgYWxsIGFjdGl2ZSBzdGF0aW9ucyBieSBsZWZ0IGpvaW5pbmcgb24gdGhlIGFjdGl2ZSBzdGF0aW9ucyBhbmQgYWxsIHN0YXRpb25zLCBpZ25vcmluZyBhbnkgcm93cyB0aGF0IGNvbnRhaW4gbWlzc2luZyB2YWx1ZXMKc3RhdGlvbl9sb2MgPC0gZHBseXI6OmxlZnRfam9pbihzdGF0aW9uX2xvYywgaW5kZWdvX3N0YXRpb25zLCBieSA9IGMoInN0YXJ0X3N0YXRpb25faWQiID0gIlN0YXRpb24uSUQiKSkgJT4lIG5hLm9taXQgJT4lIC5bLCAtNl0KIyByZW5hbWUgY29sdW1ucyBvZiBkYXRhZnJhbWUKY29sbmFtZXMoc3RhdGlvbl9sb2MpIDwtIGMoImlkIiwgImxhdCIsICJsb25nIiwgIm5hbWUiLCAiZGF0ZSIpCiMgcmVtb3ZlIHdoaXRlc3BhY2VzIGluIHRoZSBkYXRlIGNvbHVtbgpzdGF0aW9uX2xvYyRkYXRlID0gdHJpbShzdGF0aW9uX2xvYyRkYXRlICU+JSBhcy5jaGFyYWN0ZXIoKSkKCiMgYmVsb3cgYXJlIHR3byBhdXhpbGlhcnkgZnVjdGlvbiB0byBwYXJzZSB0aGUgc3RhcnQvZW5kIHRpbWUgYW5kIGRhdGUgb2YgZWFjaCB0cmlwLCB3aGljaCBkaXNwbGF5cyB0aGVtIGFzIGEgc2luZ2xlIHN0cmluZy4gV2UgcGFydGl0aW9uIHRoZW0gaW50byBzZXBhcmF0ZSBjb2x1bW5zIHRoYXQgbm93IGRpc3BsYXkgeWVhciwgbW9udGgsIGRheSwgaG91ciwgbWludXRlcywgYW5kIGRhdGUgKFlZWVktTU0tREQpLgoKIyBmb3IgcXVhcnRlcnMgb2YgMjAxNQpwYXJzZVRpbWVfMTUgPC0gZnVuY3Rpb24oZGYpIHsKICBkZiRzX2hvdXIgPC0gZm9ybWF0KGFzLlBPU0lYY3Qoc3RycHRpbWUoZGYkc3RhcnRfdGltZSwiJW0vJWQvJXkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlSCIpCiAgZGYkc19taW4gPC0gZm9ybWF0KGFzLlBPU0lYY3Qoc3RycHRpbWUoZGYkc3RhcnRfdGltZSwiJW0vJWQvJXkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlTSIpCiAgZGYkZV9ob3VyIDwtIGZvcm1hdChhcy5QT1NJWGN0KHN0cnB0aW1lKGRmJGVuZF90aW1lLCIlbS8lZC8leSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVIIikKICBkZiRlX21pbiA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRlbmRfdGltZSwiJW0vJWQvJXkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlTSIpCiAgZGYkeWVhciA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRzdGFydF90aW1lLCIlbS8lZC8leSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVZIikKICBkZiRtb250aCA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRzdGFydF90aW1lLCIlbS8lZC8leSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVtIikKICBkZiRkYXkgPC0gZm9ybWF0KGFzLlBPU0lYY3Qoc3RycHRpbWUoZGYkc3RhcnRfdGltZSwiJW0vJWQvJXkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlZCIpCiAgZGYkZGF0ZSA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRzdGFydF90aW1lLCIlbS8lZC8leSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVZLSVtLSVkIikKICByZXR1cm4oZGYpCn0KCiMgZm9yIHF1YXJ0ZXJzIG9mIDIwMTYsIHdoaWNoIGFyZSBkZWZpbmVkIGRpZmZlcmVudGx5IGZyb20gMjAxNQpwYXJzZVRpbWVfMTYgPC0gZnVuY3Rpb24oZGYpIHsKICBkZiRzX2hvdXIgPC0gZm9ybWF0KGFzLlBPU0lYY3Qoc3RycHRpbWUoZGYkc3RhcnRfdGltZSwiJW0vJWQvJVkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlSCIpCiAgZGYkc19taW4gPC0gZm9ybWF0KGFzLlBPU0lYY3Qoc3RycHRpbWUoZGYkc3RhcnRfdGltZSwiJW0vJWQvJVkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlTSIpCiAgZGYkZV9ob3VyIDwtIGZvcm1hdChhcy5QT1NJWGN0KHN0cnB0aW1lKGRmJGVuZF90aW1lLCIlbS8lZC8lWSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVIIikKICBkZiRlX21pbiA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRlbmRfdGltZSwiJW0vJWQvJVkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlTSIpCiAgZGYkeWVhciA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRzdGFydF90aW1lLCIlbS8lZC8lWSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVZIikKICBkZiRtb250aCA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRzdGFydF90aW1lLCIlbS8lZC8lWSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVtIikKICBkZiRkYXkgPC0gZm9ybWF0KGFzLlBPU0lYY3Qoc3RycHRpbWUoZGYkc3RhcnRfdGltZSwiJW0vJWQvJVkgJUg6JU0iLHR6PSIiKSkgLGZvcm1hdCA9ICIlZCIpCiAgZGYkZGF0ZSA8LSBmb3JtYXQoYXMuUE9TSVhjdChzdHJwdGltZShkZiRzdGFydF90aW1lLCIlbS8lZC8lWSAlSDolTSIsdHo9IiIpKSAsZm9ybWF0ID0gIiVZLSVtLSVkIikKICByZXR1cm4oZGYpCn0KCiMgcGFzcyBpbnRvIHJlc3BlY3RpdmUgYXV4aWxpYXJ5IGZ1bmN0aW5vcwpZMTVRMiA8LSBwYXJzZVRpbWVfMTUoWTE1UTIpClkxNVEzIDwtIHBhcnNlVGltZV8xNShZMTVRMykKWTE1UTQgPC0gcGFyc2VUaW1lXzE1KFkxNVE0KQpZMTZRMSA8LSBwYXJzZVRpbWVfMTYoWTE2UTEpClkxNlEyIDwtIHBhcnNlVGltZV8xNihZMTZRMikKWTE2UTMgPC0gcGFyc2VUaW1lXzE2KFkxNlEzKQpZMTZRNCA8LSBwYXJzZVRpbWVfMTYoWTE2UTQpCgojIGNvbWJpbmUgYWxsIHF1YXJ0ZXJseSBkYXRhIGludG8gYSBzaW5nbGUgZGF0YSBmcmFtZSBjYWxsZWQgZGYKZGYgPC0gcmJpbmQoWTE1UTIsIFkxNVEzLCBZMTVRNCwgWTE2UTEsIFkxNlEyLCBZMTZRMywgWTE2UTQpCgojIHN0YW5kYXJkaXplIGR1cmF0aW9uIGludG8gdW5pdHMgb2Ygc2Vjb25kcwojIGFsc28gcmVtb3ZlcyBjb2x1bW5zIG5vdCBuZWVkZWQgaW4gc3Vic2VxdWVudCBhbmFseXNlcwpkYXRhQ2xlYW4gPC0gZnVuY3Rpb24oZGYpIHsKICBkZiRkdXJhdGlvbiA8LSBkZiRkdXJhdGlvbiAvIDYwCiAgZGZbLCAtYygxLCAzLCA0LCA2LCA3LCA5LCAxMCwgMTIpXQp9CmRmIDwtIGRhdGFDbGVhbihkZikKCiMgcmVtb3ZlIGluZGl2aWR1YWwgZGF0YWZyYW1lcwpybShZMTVRMiwgWTE1UTMsIFkxNVE0LCBZMTZRMSwgWTE2UTIsIFkxNlEzLCBZMTZRNCkKYGBgCgpEdXJpbmcgb3VyIGluaXRpYWwgZXhwbG9yYXRpb24gb2YgdGhlIGRhdGFzZXQsIHdlIHJlYWxpemVkIHNvbWUgb2YgdGhlIHJlc3BlY3RpdmUgZGF0YXNldHMgZm9yIHRoZSBxdWFydGVybHkgZGF0YSBjb250YWluZWQgbWlzc2luZyB2YWx1ZXMsIHdoaWNoIHdvdWxkIGltcGx5IHRoYXQgb3VyIGZpbmFsIGRhdGFmcmFtZSBhbHNvIGNvbnRhaW5zIG1pc3NpbmcgdmFsdWVzLiBXZSB0aHVzIHRyeSB0byBxdWFsaXRhdGl2ZWx5IHZpc3VhbGl6ZSB0aGVzZSBmZWF0dXJlcywgc2hvdyBiZWxvdzoKCmBgYHtyIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTV9CiMgaWdub3JlIGNvbHVtbnMgYWZ0ZXIgcGFzc2hvbGRlcl90eXBlIChjb2xfbnVtID0gMTQpIGFzIHN1YnNlcXVlbnQgY29sdW1ucyBhcmUgZGVmaW5lZCBieSB1cy4KbGlicmFyeShleHRyYWNhdCkKdmlzbmEoZGZbLGMoMToxNCldKQpgYGAKCmBgYHtyfQptaXNzaW5nX3RhYmxlID0gdGFibGUoaXMubmEoZGYpKQpgYGAKCmBgYHtyfQpjYXQoIlBlcmNlbnRhZ2Ugb2YgbWlzc2luZyBlbnRyaWVzOiIsIG1pc3NpbmdfdGFibGVbMl0vc3VtKG1pc3NpbmdfdGFibGUpKjEwMCkKYGBgCgpXZSBzZWUgdGhhdCB0aGUgcHJvcG9ydGlvbiBvZiBtaXNzaW5nIHZhbHVlcyBpcyBtaW5pc2N1bGUgcmVsYXRpdmUgdG8gb3VyIHdob2xlIGRhdGFzZXQgKGFwcHJveGltYXRlbHkgMC4wMTcyJSkuIE1vcmVvdmVyLCB0aGV5IGFyZSBhbGwgcmVsYXRlZCB0byBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIGRhdGEgYXMgd2VsbCBhcyB0aGUgYmlrZSBJRC4gSWYgbmVlZCBiZSwgd2UgY2FuIGluZmVyIHRoZSBsYXRpdHVkZSBhbmQgbG9uZ2l0dWRlIGZyb20gdGhlIHN0YXRpb24gbmFtZSBvciBzdGF0aW9uIElELCBidXQgd2UgYWN0aXZlbHkgY2hvc2UgdG8gaWdub3JlIHJvd3MgY29ycmVzcG9uZGluZyB0byB0aGVzZSBtaXNzaW5nIHZhbHVlcyBzaW5jZSB3ZSBjYW5ub3QgZXhwbGljYXRlIHdoeSB0aGVzZSB2YWx1ZXMgYXJlIG1pc3NpbmcuIFdlIHRodXMgY2hvb3NlIHRvIGlnbm9yZSB0aGVtIGFsdG9nZXRoZXIuCgojIyMjIEluZGVnbyBzdGF0aW9ucyBkYXRhCgpGb3IgdGhlIGRhdGEgZGV0YWlsaW5nIHRoZSBpbmZvcm1hdGlvbiBvZiBlYWNoIHN0YXRpb24gKGluZGVnb19zdGF0aW9ucy5jc3YpLCB3ZSBoYWQgdG8gcGVyZm9ybSBhIGJpdCBvZiBkYXRhIHByZXByb2Nlc3NpbmcgdG8gZ2V0IGl0IGludG8gYW4gaWRlYWwgZm9ybWF0LiBUaGUgZGF0YSBjb250YWluZWQgdGhlIGNvbHVtbnMgYXMgZm9sbG93czoKCiogU3RhdGlvbiBJRCAodW5pcXVlIGlkZW50aWZpZXIgb2YgYSBJbmRlZ28gc3RhdGlvbikKKiBTdGF0aW9uIE5hbWUgKG5hbWUgb2YgY29ycmVzcG9uZGluZyBJbmRlZ28gc3RhdGlvbiwgdXN1YWxseSBhIHN0cmVldCBhZGRyZXNzKQoqIEdvIGxpdmUgZGF0ZSAodGhlIGRhdGUgdGhhdCB0aGUgc3RhdGlvbiBzdGFydGVkIG9wZXJhdGlvbnMpCiogU3RhdHVzIChpdHMgY3VycmVudCBzdGF0dXMsIGVpdGhlciBhY3RpdmUgb3IgaW5hY3RpdmUpCgpUaGVyZSB3ZXJlIG1hbnkgbGVhZGluZyBhbmQgdHJhaWxpbmcgd2hpdGVzcGFjZXMgaW4gdGhlIDxpPlN0YXRpb24gTmFtZTwvaT4gY29sdW1uLCBzbyBwZXJmb3JtZWQgdGhlIGZvbGxvd2luZyB0byByZW1vdmUgdGhlc2UgdW5uZWVkZWQgY2hhcmFjdGVyczoKCmBgYHtyfQojIGxvYWQgc3RhdGlvbnMgZGF0YQppbmRlZ29fc3RhdGlvbnMgPC0gcmVhZC5jc3YoIi4uL2RhdGEvaW5kZWdvX3N0YXRpb25zLmNzdiIsIGhlYWRlcj1UKQojIGEgZnVuY3Rpb24gdGhhdCByZW1vdmVzIGxlYWRpbmcgYW5kIHRyYWlsaW5nIHdoaXRlIHNwYWNlcyBvZiBhIGdpdmVuIHN0cmluZwp0cmltIDwtIGZ1bmN0aW9uICh4KSBnc3ViKCJeXFxzK3xcXHMrJCIsICIiLCB4KQojIHJlbW92ZSB3aGl0ZXNwYWNlcyBzdXJyb3VuZGluZyBlYWNoIGVudHJ5IGluIHRoZSBjb2x1bW4gb2YgU3RhdGlvbi5OYW1lCmluZGVnb19zdGF0aW9ucyRTdGF0aW9uLk5hbWUgPC0gdHJpbShpbmRlZ29fc3RhdGlvbnMkU3RhdGlvbi5OYW1lKQpgYGAKClRoZXJlIHdlcmUgbm8gbWlzc2luZyB2YWx1ZXMgaW4gdGhpcyB0YWJsZSwgc28gd2UgZG8gbm90IHByb3ZpZGUgcGxvdHMgcmVsYXRlZCB0byB0aGlzIGhlcmUuCgojIyMjIEdlb0pTT04KCkZvciByZWFsLXRpbWUgc3RhdHVzIHVwZGF0ZXMgb2YgSW5kZWdvIHN0YXRpb25zLCB3ZSB1c2UgdGhlIHNlbWktc3RydWN0dXJlZCBbR2VvSlNPTiBkYXRhc2V0XShodHRwczovL3d3dy5yaWRlaW5kZWdvLmNvbS9zdGF0aW9ucy9qc29uLykgcHJvdmlkZWQgYnkgSW5kZWdvLiBXZSBhbGxvdyB0aGUgdXNlciB0byBtYW51YWxseSByZWZyZXNoIG91ciBTaGlueSBhcHAsIGluIHdoaWNoIHdlIGNhbGwgdGhlIEdlb0pTT04gZmlsZSBpbiByZWFsLXRpbWUgdG8gZ2V0IGEgdmlzdWFsaXphdGlvbiB1cGRhdGUgKG1vcmUgc3BlY2lmaWMgZGV0YWlscyBpbiBzdWJzZXF1ZW50IHNlY3Rpb25zKS4gV2UgdXNlZCB0aGUgUiBwYWNrcGFnZSBbZ2VvanNvbmlvXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZ2VvanNvbmlvL2luZGV4Lmh0bWwpIHRvIHVucGFjayBhbmQgdHJhbnNmb3JtIHRoaXMgZGF0YSBpbnRvIGEgd29ya2FibGUgZm9ybWF0IGFzIGZvbGxvd3M6CgpgYGB7cn0KbGlicmFyeShnZW9qc29uaW8pCnN0YXRpb25fc3RhdHVzIDwtIGZyb21KU09OKCJodHRwczovL2diZnMuYmN5Y2xlLmNvbS9iY3ljbGVfaW5kZWdvL3N0YXRpb25fc3RhdHVzLmpzb24iKQpgYGAKCiMjIEV4ZWN1dGl2ZSBTdW1tYXJ5CgoKVGhlIHByaW1hcnkgb2JqZWN0aXZlIG9mIHRoaXMgcmVwb3J0IGlzIHRvIGRlcml2ZSB2YWx1YWJsZSBpbnNpZ2h0cyBvZiBkZXBsb3lpbmcgc2hhcmVkIGJpa2VzIGZvciBiaWcgY2l0aWVzIChpbiBvdXIgY2FzZSwgUGhpbGFkZWxwaGlhJ3MgSW5kZWdvKSwgYW5kIGhvdyB0aGV5IGFyZSB1dGlsaXplZCBieSB0aGUgcHVibGljIG1hc3MuIE1vcmUgc3BlY2lmaWNhbGx5LCB3ZSB3YW50IHRvIHVuZGVyc3RhbmQgcGF0dGVybnMgb2YgdXNhZ2UgYW1vbmcgZGlmZmVyZW50IHN0YXRpb25zIGFuZCB1c2VycyBvdmVyIHRpbWUuIFRoZSBleHBsb3JhdG9yeSBtZXRob2RvbG9naWVzIHRoYXQgd2UgaW1wbGVtZW50IG9uIHRoZSBJbmRlZ28gZGF0YXNldCB3aWxsIGdlbmVyYWxseSBmb2xsb3cgY29udmVudGlvbmFsIHByaW5jaXBsZXMgb2YgYW5hbHl6aW5nIHRlbXBvcmFsIGFuZCBzcGF0aWFsIGZlYXR1cmVzIG9mIGxhcmdlIGRhdGFzZXRzLiAKClVwb24gcmV0cmlldmluZyB0aGUgZGF0YXNldCwgd2UgaW1tZWRpYXRlbHkgcmFpc2VkIHRoZSBmb2xsb3dpbmcgcXVlc3Rpb25zOgoKMS4gRm9yIHRoZSB0b3RhbCAxMDUgc3RhdGlvbnMgZGVwbG95ZWQgZ3JhZHVhbGx5IHdpdGhpbiB0d28geWVhcnMsIHdoaWNoIHN0YXRpb25zIGFyZSB0aGUgbW9zdCBwb3B1bGFyIG9uZXM/IEhhcyB0aGUgcG9wdWxhcml0eSBjaGFuZ2VkIG92ZXIgdGltZT8KCjIuIEZvciBzcGVjaWZpYyBzdGF0aW9ucyB3ZSBoYXZlIGFuIGV4cGVjdGlvbiBvbiB0aGVpciBkYWlseSByZW50YWwgcGF0dGVybnMuIEhvdyBkbyB3ZSB2aXN1YWxpemUgYW5kIGV4YW1pbmUgc3VjaCBwYXR0ZXJucz8KCjMuIFdpdGggdGhlIGdlb2dyYXBoaWNhbCBkYXRhIG9mIHN0YXRpb25zIGFuZCBHZW9TT04gZmlsZSwgaG93IGRvIHdlIGRyYXcgY29ubmVjdGlvbnMgYW1vbmcgc3RhdGlvbnMgYW5kIHNlZSByZW50YWwgYW5kIHJldHVybiBwYXR0ZXJucywgcG9wdWxhciByb3V0ZXMsIGFuZCB2aXN1YWxpemUgdGhlIHJlYWwtdGltZSBzdGF0aW9uIHN0YXR1cz8KCldlIHByZXNlbnQgaGVyZSBhIHF1aWNrIHJ1bi10aHJvdWdoIG9mIHRoZSBwbG90cyB3ZSBwcmVzZW50IGluIG91ciBtYWluIGFuYWx5c2lzLCB3aGVyZSB3ZSB3aWxsIGRpdmUgaW50byBtdWNoIGZpbmVyIGRldGFpbHMuIFRoZSBmb2xsb3dpbmcgcGxvdHMgZGVtb25zdHJhdGUgdGhlIG1haW4gZmluZGluZ3Mgb2Ygb3VyIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMuCgpGaXJzdGx5LCB3ZSBtYWtlIGdlbmVyYWwgbGluZSBwbG90cyB3aXRoIG1vbnRobHkgZnJlcXVlbmN5IGRhdGEgYW5kIGZ1cnRoZXIgc2VwYXJhdGUgdGhlIGRhdGEgaW50byB3ZWVrIGRheXMgYW5kIHdlZWtlbmRzLiBGcm9tIHRoZSBwbG90cywgd2Ugc2VlIGNsZWFybHkgdGhlIHNlYXNvbmFsIHVzYWdlIHZhcmlhdGlvbi4KIVtdKC9Vc2Vycy9tYXNvbnN1bi9EZXNrdG9wL1NUQVQgNTcwMi9wcm9qZWN0L3BpYy9mcmVxLnBuZykKClNlY29uZGx5LCB3ZSBwaWNrIHRocmVlIGRpZmZlcmVudCBzdGF0aW9ucyBsb2NhdGVkIGF0IHRoZSB1bml2ZXJzaXR5IGFyZWEsIGRvd250b3duIGFuZCBzdWJ1cmJhbiBQaGlsYWRlbHBoaWEsIGFuZCBkcmF3IHRoZSBhdmVyYWdlIGhvdXJseSB0cmlwIGR1cmF0aW9ucyBmb3Igd2VlayBkYXlzIGFuZCB3ZWVrZW5kcyBpbiBhIHNlbGVjdGVkIHRpbWUgcGVyaW9kLiBUcmF2ZWwgcGF0dGVybnMgYW1vbmcgZGlmZmVyZW50IHR5cGVzIG9mIHN0YXRpb25zIHRodXMgYmVjb21lcyBldmlkZW50LiBGb3IgZXhhbXBsZSwgdGhlIHdlZWtlbmRzJyBhdmVyYWdlIHRyaXAgZHVyYXRpb24gaW4gdGhlIGFmdGVybm9vbiBpcyBsb25nZXIgdGhhbiB0aGF0IG9mIHRoZSB3ZWVrZGF5J3MgZm9yIHVzZXJzIGNsb3NlIHRvIHVuaXZlcnNpdHkgY2FtcHVzZXMuCiFbXSgvVXNlcnMvbWFzb25zdW4vRGVza3RvcC9TVEFUIDU3MDIvcHJvamVjdC9zdGF0aW9ucy5wbmcpCgpUaGlyZGx5LCB3ZSB1dGlsaXplIGBsZWFmbGV0YCB0byBjcmVhdGUgaW50ZXJhY3RpdmUgbWFwcyBpbiBvcmRlciB0byByZXByZXNlbnQgdGhlIHNwYXRpYWwgaW5mb3JtYXRpb24gd2UgY2FuIGRlcml2ZSBmcm9tIHRoZSBkYXRhc2V0LiBXZSBlZmZlY3RpdmVseSByZXByZXNlbnRlZCB0aGUgYXZlcmFnZSBkYWlseSByZW50YWwgYW5kIHJldHVybiBmcmVxdWVuY2llcyBhbW9uZyBzdGF0aW9ucyB1c2luZyBkaWZmZXJlbnQgZG90IHNpemVzIChsYXJnZXIgZG90cyBpbXBsaWVzIGhpZ2hlciBmcmVxdWVuY3kpLiBXZSBlbWJlZCB0aGUgcmVhbC10aW1lIEdlb0pTT04gZmlsZSBpbnRvIG91ciBgU2hpbnlgIGFwcCB0byBzaG93IHJlYWwtdGltZSB1cGRhdGVzIG9mIHN0YXRpb24gc3RhdHVzLiBJbiBvcmRlciB0byBqb2ludGx5IGFuYWx5emUgdGhlIHNwYXRpYWwgYW5kIHRpbWUgZGltZW5zaW9ucywgd2UgcGljayB0aGUgdGVuIG1vc3QgcG9wdWxhciByZW50YWwgYW5kIHJldHVybiBzdGF0aW9ucyBmb3IgdHdvIHJlY2VudCBxdWFydGVycyB0byBkZXRlcm1pbmUgZGlmZmVyZW5jZXMgaW4gdHJlbmRzLiBCZXNpZGVzIHRoZSBwbG90cyBiZWxvdywgd2UgYWxzbyBtYWRlIGEgbWFwIGZvciB0aGUgZml2ZSBtb3N0IHBvcHVsYXIgcm91dGVzIChzZWUgbmV4dCBwYXJ0KS4KIVtdKC9Vc2Vycy9tYXNvbnN1bi9EZXNrdG9wL1NUQVQgNTcwMi9wcm9qZWN0L3BpYy9tYXAucG5nKQoKTW9yZW92ZXIsIHdlIGFuYWx5emUgdGhlIGRhaWx5IHRyaXAgZHVyYXRpb25zIGFuZCB0aGUgdXNlciBwcm9maWxlcywgYW5kIHVuY292ZXIgdGhlIHRoZSBzaGlmdCBvZiBwYXNzaG9sZGVyIHR5cGVzIGFuZCB0cmlwIHJvdXRlIChvbmUtd2F5IG9yIHJvdW5kLXRyaXApIGNoYW5nZXMuIFRoZSBtb3N0IGluY2lzaXZlIG9ic2VydmF0aW9uIHdlIGdvdCBmcm9tIHRoaXMgc2VjdGlvbiBpcyB0aGF0IHRoZSBwZXJjZW50YWdlIG9mIHRoZSBtb3N0IHBvcHVsYXIgcGFzc2hvbGRlciB0eXBlIChJbmRlZ28zMCkgYW5kIG9uZS13YXkgdHJpcHMgaGFzIGdvdHRlbiBtb3JlIHBvcHVsYXIuIEluIGFkZGl0aW9uLCBsZXNzIHBlb3BsZSBhcmUgdGFraW5nIHJvdW5kIHRyaXBzLgoKIVtdKC9Vc2Vycy9tYXNvbnN1bi9EZXNrdG9wL1NUQVQgNTcwMi9wcm9qZWN0L3BpYy90eXBlLnBuZykKCkxhc3RseSwgd2UgY3JlYXRlIGEgYFNoaW55YCBkYXNoYm9hcmQgdG8gbGV0IHBlb3BsZSBleHBsb3JlIHRoZSBkYXRhc2V0IGFzIHRoZWlyIHdpc2hlcy4gVGhhdCBpcywgdGhleSBjYW4gc2VsZWN0IHN0YXRpb25zIGFuZCBkYXRlIHJhbmdlcyBvZiBpbnRlcmVzdCwgYW5kIGFsc28gbW9kaWZ5IHBhcmFtZXRlcnMgb2YgZGlmZmVyZW50IGNoYXJ0cyAoZS5nLiBudW1iZXIgb2YgYmlucyBmb3IgaGlzdG9ncmFtcykgdG8gZ2VuZXJhdGUgY3VzdG9taXplZCBpbnNpZ2h0cy4gCiFbXSgvVXNlcnMvbWFzb25zdW4vRGVza3RvcC9TVEFUIDU3MDIvcHJvamVjdC9waWMvc2hpbnkucG5nKQoKIyMgTWFpbiBhbmFseXNpcyAKCkluIHRoaXMgbWFpbiBzZWN0aW9uLCB3ZSBwcmVzZW50IG91ciBmaW5kaW5ncyBvZiBkaWZmZXJlbnQgZmFjZXRzIHBlcnRhaW5pbmcgdG8gdGhlIEluZGVnbyBkYXRhc2V0LiBXZSBzdWJkaXZpZGUgb3VyIGFuYWx5c2lzIGludG8gZm91ciBpbnRlcnJlbGF0ZWQgc2VjdGlvbnM6IHN0YXRpb24gdXNhZ2UsIGRhaWx5IHRyYWZmaWMsIHRyaXAgZHVyYXRpb24sIGFuZCB1c2VyIHByb2ZpbGVzLCBpbiB0aGF0IHBhcnRpY3VhbHIgb3JkZXIuIFdlIHdpbGwgd2FsayB0aGUgcmVhZGVyIHRocm91Z2ggb3VyIHRob3VnaHQgcHJvY2VzcyBhbmQgZmluZGluZ3MsIGFuZCBmaW5hbGx5IHByZXNlbnQgc29tZSBjb25jbHVkaW5nIHJlbWFya3MuCgojIyMjIFN0YXRpb24gdXNhZ2UKCkluIHRoaXMgc2VjdGlvbiwgd2UgZXhhbWluZSB0aGUgdXNhZ2UgcmF0ZSBhbW9uZyB0aGUgZGlmZmVyZW50IEluZGVnbyBzdGF0aW9ucyBhY3Jvc3MgUGhpbGFkZWxwaGlhLiBXZSBtb3RpdmF0ZSBvdXIgYW5hbHlzaXMgZnJvbSBhIGhvbGlzdGljIHBlcnNwZWN0aXZlIHRvd2FyZHMgbW9yZSBncmFudWxhciBhbmFseXNpcy4gVGhpcyBzdHJ1Y3R1cmFsIGxheW91dCBhbHNvIGhvbGRzIHRydWUgZm9yIHN1YnNlcXVlbnQgc2VjdGlvbnMuIFdpdGggdGhhdCBpbiBtaW5kLCBsZXQgdXMgZmlyc3QgcGF0dGVybnMgcmVsYXRlZCB0byBzb21lIG9mIHRoZSBtb3JlIHBvcHVsYXIgSW5kZWdvIHN0YXRpb25zLiBXZSBzdWJkaXZpZGUgb3VyIGFuYWx5c2lzIHRvIHRoYXQgb2YgcmVudGFsIGFuZCByZXR1cm4gc3RhdGlvbnMsIHdoaWNoIHJlZmxlY3QgcG9wdWxhciBzdGFydGluZyBhbmQgdGVybWluYWwgZGVzdGluYXRpb25zIHRoYXQgcGVvcGxlIGVsZWN0IHRvIGNob29zZSBpbiBQaGlsYWRlbHBoaWEuCgpgYGB7ciByZXN1bHRzPSJoaWRlIixmaWcud2lkdGg9NSwgZmlnLmhlaWdodD00fQojIFBvcHVsYXIgU3RhdGlvbnMgLSBoaXN0b2dyYW0KbGlicmFyeShncmlkRXh0cmEpCnBvcHVsYXJfc3RhdGlvbiA8LSBkZiAlPiUgZ3JvdXBfYnkoeWVhciwgbW9udGgsIHN0YXJ0X3N0YXRpb25faWQpICU+JSBzdW1tYXJpemUocmVudGFsID0gbigpKQpjb2xuYW1lcyhwb3B1bGFyX3N0YXRpb24pWzNdIDwtICJpZCIKcG9wdWxhcl9zdGF0aW9uJGlkIDwtIGFzLmNoYXJhY3Rlcihwb3B1bGFyX3N0YXRpb24kaWQpCmZvbyA8LSBkZiAlPiUgZ3JvdXBfYnkoeWVhciwgbW9udGgsIGVuZF9zdGF0aW9uX2lkKSAlPiUgc3VtbWFyaXplKHJldHVybiA9IG4oKSkKY29sbmFtZXMoZm9vKVszXSA8LSAiaWQiCmZvbyRpZCA8LSBhcy5jaGFyYWN0ZXIoZm9vJGlkKQoKcG9wdWxhcl9zdGF0aW9uIDwtIHBvcHVsYXJfc3RhdGlvbiAlPiUgZnVsbF9qb2luKGZvbywgYnkgPSBjKCJpZCIgPSAiaWQiLCAieWVhciIgPSAieWVhciIsICJtb250aCIgPSAibW9udGgiKSkKcm0oZm9vKQpwb3B1bGFyX3N0YXRpb24gPC0gdGlkeXI6OmdhdGhlcihwb3B1bGFyX3N0YXRpb24sIHR5cGUsIGNvdW50LCBjKDQsNSkpCmNvbG5hbWVzKHBvcHVsYXJfc3RhdGlvbilbM10gPC0gImlkIgpwb3B1bGFyX3N0YXRpb25baXMubmEocG9wdWxhcl9zdGF0aW9uKV0gPC0gMApwb3B1bGFyX3N0YXRpb24gPC0gcG9wdWxhcl9zdGF0aW9uW3BvcHVsYXJfc3RhdGlvbiRpZCE9IiNOL0EiLF0Kc3RhdGlvbl9sb2MkaWQgPC0gYXMuY2hhcmFjdGVyKHN0YXRpb25fbG9jJGlkKQpyZWR1Y2VkX3N0YXRpb24gPC0gcG9wdWxhcl9zdGF0aW9uICU+JSBsZWZ0X2pvaW4oc3RhdGlvbl9sb2MsIGJ5PWMoImlkIj0iaWQiKSkgJT4lIG5hLm9taXQKcmVkdWNlZF9zdGF0aW9uJHF1YXJ0ZXIgPC0gKGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHJlZHVjZWRfc3RhdGlvbiRtb250aCkpIC0gMSkgJS8lIDMgKyAxCnJlZHVjZWRfc3RhdGlvbiA8LSByZWR1Y2VkX3N0YXRpb24gJT4lIGdyb3VwX2J5KGlkLCBuYW1lLCBsYXQsIGxvbmcsIHR5cGUsIHllYXIsIHF1YXJ0ZXIpICU+JSBzdW1tYXJpemUoY291bnQgPSBzdW0oY291bnQpKQoKIyBFYXJsaWVzdCBxdWFydGVyOiAyMDE1UTIsIExhdGVzdCBxdWFydGVyOiAyMDE2UTQKcmVkdWNlZF9zdGF0aW9uJHllYXIgPC0gYXMubnVtZXJpYyhyZWR1Y2VkX3N0YXRpb24keWVhcikKcnNfZmlyc3RfcmVudGFsIDwtIHJlZHVjZWRfc3RhdGlvbltyZWR1Y2VkX3N0YXRpb24keWVhcj09MjAxNSZyZWR1Y2VkX3N0YXRpb24kcXVhcnRlcj09MiZyZWR1Y2VkX3N0YXRpb24kdHlwZT09InJlbnRhbCIsXQpyc19sYXN0X3JlbnRhbCA8LSByZWR1Y2VkX3N0YXRpb25bcmVkdWNlZF9zdGF0aW9uJHllYXI9PTIwMTYmcmVkdWNlZF9zdGF0aW9uJHF1YXJ0ZXI9PTQmcmVkdWNlZF9zdGF0aW9uJHR5cGU9PSJyZW50YWwiLF0KcnNfZmlyc3RfcmV0dXJuIDwtIHJlZHVjZWRfc3RhdGlvbltyZWR1Y2VkX3N0YXRpb24keWVhcj09MjAxNSZyZWR1Y2VkX3N0YXRpb24kcXVhcnRlcj09MiZyZWR1Y2VkX3N0YXRpb24kdHlwZT09InJldHVybiIsXQpyc19sYXN0X3JldHVybiA8LSByZWR1Y2VkX3N0YXRpb25bcmVkdWNlZF9zdGF0aW9uJHllYXI9PTIwMTYmcmVkdWNlZF9zdGF0aW9uJHF1YXJ0ZXI9PTQmcmVkdWNlZF9zdGF0aW9uJHR5cGU9PSJyZXR1cm4iLF0KCnJzX2ZpcnN0X3JlbnRhbCA8LSByc19maXJzdF9yZW50YWxbb3JkZXIoLXJzX2ZpcnN0X3JlbnRhbCRjb3VudCksXVsxOjEwLF0KcnNfbGFzdF9yZW50YWwgPC0gcnNfbGFzdF9yZW50YWxbb3JkZXIoLXJzX2xhc3RfcmVudGFsJGNvdW50KSxdWzE6MTAsXQpyc19maXJzdF9yZXR1cm4gPC0gcnNfZmlyc3RfcmV0dXJuW29yZGVyKC1yc19maXJzdF9yZXR1cm4kY291bnQpLF1bMToxMCxdCnJzX2xhc3RfcmV0dXJuIDwtIHJzX2xhc3RfcmV0dXJuW29yZGVyKC1yc19sYXN0X3JldHVybiRjb3VudCksXVsxOjEwLF0KCnJzX2ZpcnN0X3JlbnRhbCRuYW1lIDwtIGZhY3Rvcihyc19maXJzdF9yZW50YWwkbmFtZSwgbGV2ZWxzID0gcnNfZmlyc3RfcmVudGFsJG5hbWVbb3JkZXIocnNfZmlyc3RfcmVudGFsJGNvdW50KV0pCnJzX2xhc3RfcmVudGFsJG5hbWUgPC0gZmFjdG9yKHJzX2xhc3RfcmVudGFsJG5hbWUsIGxldmVscyA9IHJzX2xhc3RfcmVudGFsJG5hbWVbb3JkZXIocnNfbGFzdF9yZW50YWwkY291bnQpXSkKcnNfZmlyc3RfcmV0dXJuJG5hbWUgPC0gZmFjdG9yKHJzX2ZpcnN0X3JldHVybiRuYW1lLCBsZXZlbHMgPSByc19maXJzdF9yZXR1cm4kbmFtZVtvcmRlcihyc19maXJzdF9yZXR1cm4kY291bnQpXSkKcnNfbGFzdF9yZXR1cm4kbmFtZSA8LSBmYWN0b3IocnNfbGFzdF9yZXR1cm4kbmFtZSwgbGV2ZWxzID0gcnNfbGFzdF9yZXR1cm4kbmFtZVtvcmRlcihyc19sYXN0X3JldHVybiRjb3VudCldKQoKcDEgPC0gZ2dwbG90KHJzX2ZpcnN0X3JlbnRhbCwgYWVzKHggPSBuYW1lLCB5ID0gY291bnQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIGNvbG9yPSJibHVlIiwgZmlsbD0iYmx1ZSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPWNvdW50KSwgaGp1c3Q9LTAuMDUsIHNpemU9Mi41KSArCiAgY29vcmRfZmxpcCgpICsKICBnZ3RpdGxlKCJNb3N0IFBvcHVsYXIgUmVudGFsIFN0YXRpb24gMjAxNVEyIikgKwogIHhsYWIoIkZyZXF1ZW5jeSIpICsKICB5bGFiKCJTdGF0aW9uIikgKwogIHNjYWxlX2NvbG9yX2ZpdmV0aGlydHllaWdodCgiY3lsIikgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjUsMC41LDAuNSwwLjUpLCAiY20iKSkKCnAyIDwtIGdncGxvdChyc19sYXN0X3JlbnRhbCwgYWVzKHggPSBuYW1lLCB5ID0gY291bnQpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICdpZGVudGl0eScsIGNvbG9yPSJibHVlIiwgZmlsbD0iYmx1ZSIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsPWNvdW50KSwgaGp1c3Q9LTAuMDUsIHNpemU9Mi41KSArCiAgY29vcmRfZmxpcCgpICsKICBnZ3RpdGxlKCJNb3N0IFBvcHVsYXIgUmVudGFsIFN0YXRpb24gMjAxNlE0IikgKwogIHhsYWIoIkZyZXF1ZW5jeSIpICsKICB5bGFiKCJTdGF0aW9uIikgKwogIHNjYWxlX2NvbG9yX2ZpdmV0aGlydHllaWdodCgiY3lsIikgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjUsMC41LDAuNSwwLjUpLCAiY20iKSkKCnAzIDwtIGdncGxvdChyc19maXJzdF9yZXR1cm4sIGFlcyh4ID0gbmFtZSwgeSA9IGNvdW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknLCBjb2xvcj0iYmx1ZSIsIGZpbGw9ImJsdWUiKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1jb3VudCksIGhqdXN0PS0wLjA1LCBzaXplPTIuNSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgZ2d0aXRsZSgiTW9zdCBQb3B1bGFyIFJldHVybiBTdGF0aW9uIDIwMTVRMiIpICsKICB4bGFiKCJGcmVxdWVuY3kiKSArCiAgeWxhYigiU3RhdGlvbiIpICsKICBzY2FsZV9jb2xvcl9maXZldGhpcnR5ZWlnaHQoImN5bCIpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwgImNtIikpCgpwNCA8LSBnZ3Bsb3QocnNfbGFzdF9yZXR1cm4sIGFlcyh4ID0gbmFtZSwgeSA9IGNvdW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAnaWRlbnRpdHknLCBjb2xvcj0iYmx1ZSIsIGZpbGw9ImJsdWUiKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1jb3VudCksIGhqdXN0PS0wLjA1LCBzaXplPTIuNSkgKwogIGNvb3JkX2ZsaXAoKSArCiAgZ2d0aXRsZSgiTW9zdCBQb3B1bGFyIFJldHVybiBTdGF0aW9uIDIwMTZRNCIpICsKICB4bGFiKCJGcmVxdWVuY3kiKSArCiAgeWxhYigiU3RhdGlvbiIpICsKICBzY2FsZV9jb2xvcl9maXZldGhpcnR5ZWlnaHQoImN5bCIpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwgImNtIikpCgpwMSA8LSBnZ3Bsb3RfZ3RhYmxlKGdncGxvdF9idWlsZChwMSkpCnAyIDwtIGdncGxvdF9ndGFibGUoZ2dwbG90X2J1aWxkKHAyKSkKcDMgPC0gZ2dwbG90X2d0YWJsZShnZ3Bsb3RfYnVpbGQocDMpKQpwNCA8LSBnZ3Bsb3RfZ3RhYmxlKGdncGxvdF9idWlsZChwNCkpCgptYXhXaWR0aCA9IGdyaWQ6OnVuaXQucG1heChwMSR3aWR0aHNbMjozXSwgcDIkd2lkdGhzWzI6M10sIHAzJHdpZHRoc1syOjNdLCBwNCR3aWR0aHNbMjozXSkKcDEkd2lkdGhzWzI6M10gPC0gbWF4V2lkdGgKcDIkd2lkdGhzWzI6M10gPC0gbWF4V2lkdGgKcDMkd2lkdGhzWzI6M10gPC0gbWF4V2lkdGgKcDQkd2lkdGhzWzI6M10gPC0gbWF4V2lkdGgKYGBgCgpgYGB7ciBmaWcud2lkdGg9Ny41LCBmaWcuaGVpZ2h0PTV9CiMgUGxvdCBoaXN0b2dyYW1zIGZvciBzZWxlY3RlZCBxdWFydGVycwpncmlkLmFycmFuZ2UocDEsIHAzLCBwMiwgcDQsIGhlaWdodHMgPSBjKDMsIDMpKQpgYGAKCkluIHRoZSBhYm92ZSBncmFwaCwgd2Ugc2VlIHRoYXQgdGhlIG1vc3QgcG9wdWxhciBzdGF0aW9ucyBhcmUgc29tZXdoYXQgaW52YXJpYW50IHRvIHRpbWUuIFdlIGNob3NlIHRoZSBmaXJzdCBhbmQgbGF0ZXN0IHF1YXJ0ZXJzIHRvIGRldGVybWluZSBtb3N0IHBvcHVsYXIgcmVudGFsIGFuZCByZXR1cm4gc3RhdGlvbnMuIEluIGFsbCBjYXNlcywgdGhlIG1vc3QgcG9wdWxhciByZW50YWwgYW5kIHJldHVybiBzdGF0aW9uIHdhcyBbUml0dGVuaG91c2UgU3F1YXJlXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9SaXR0ZW5ob3VzZV9TcXVhcmUpLiBUaGlzIGlzIGludHVpdGl2ZSBhcyBpdCBpcyBub3Qgb25seSBhIHBvcHVsYXIgdG91cmlzdCBkZXN0aW5hdGlvbiwgYmVpbmcgaGFpbGVkIGFzICJvbmUgb2YgdGhlIGZpbmVzdCB1cmJhbiBwdWJsaWMgc3BhY2VzIGluIHRoZSBVbml0ZWQgU3RhdGVzLCIgKGF0IGxlYXN0IGFjY29yZGluZyB0byBXaWtpcGVkaWEpIGJ1dCBpdCBpcyBhbHNvIGluIGNsb3NlIHByb3hpbWl0eSB0byB0aGUgZXBpY2VudGVyIG9mIFBoaWxhZGVscGhpYS4gTW9yZSBzcGVjaWZpY2FsbHksIGluIFEyIG9mIDIwMTUsIHRoZSB0b3AgZml2ZSBtb3N0IHBvcHVsYXIgcmVudGFsIGFuZCByZXR1cm4gc3RhdGlvbnMgd2VyZSBhbGwgdGhlIHNhbWUsIHdpdGggdGhlIGV4YWN0IHNhbWUgcmFua2luZy4gVGh1cywgaXQgd291bGQgc2VlbSB0aGF0IHRoZXJlIGlzIG5vIHByZWZlcmVuY2UgZm9yIGRyb3BvZmYgb3IgcGlja3VwIHN0YXRpb25zLCBidXQgc2ltcGx5IGh1YnMgZm9yIGJpa2UgdXNlcnMuIEluIFE0IG9mIDIwMTYsIHdlIG9ic2VydmUgbWFueSBvZiB0aGUgc2FtZSByZXR1cm4gYW5kIGFuZCByZW50YWwgc3RhdGlvbnMsIGJ1dCBkaWZmZXJlbmNlcyBleGlzdC4gRm9yIGluc3RhbmNlLCBQaGlsYWRlbHBoaWEgTXVzZXVtIG9mIEFydCAoUE1PQSkgaXMgbm8gbG9uZ2VyIGluIHRoZSB0b3AgdGVuIG1vc3QgcG9wdWxhciByZXR1cm4gb3IgcmVudGFsIHN0YXRpb25zIHdoZXJlYXMgVW5pdmVyc2l0eSBDaXR5IHN0YXRpb24gaXMsIHdoaWNoIHdhcyBub3QgaW4gUTIgb2YgMjAxNS4gVGhlc2UgdmFyaWFiaWxpdHkgYXJlIGRpZmZpY3VsdCB0byBleHBsYWluIGJhc2VkIG9uIHRoZSBkYXRhc2V0IGFsb25lLCBidXQgd2Ugc3VybWlzZSBhIGNvdXBsZSBvZiBwb3NzaWJsZSBjYXVzZXMuIEZpcnN0LCB0aGUgZGVtb2dyYXBoaWMgb2YgSW5kZWdvIHVzZXJzIGhhcyBzaGlmdGVkIG92ZXIgdGltZSAoZnJvbSBpbnRyaWd1ZWQgdXNlcnMgdG8gYSB0aG9zZSB1c2luZyBpdCBmb3Igcm91dGluZSB0cmlwcykuIFNlY29uZCwgc2Vhc29uYWxpdHkgbWF5IGFmZmVjdCBwb3B1bGFyIHJlbnRhbC9yZXR1cm4gc3RhdGlvbnMuIFRoZXJlIGlzIGEgbGFyZ2VyIHRvdXJpc3QgY29ob3J0IGR1cmluZyB0aGUgc3VtbWVyIHNvIHRoYXQgbWF5IGJlIHdoeSBQTU9BIGlzIHNvIGhpZ2ggdXAgaW4gdGhlIHJhbmtpbmdzIGZvciBRMiBvZiAyMDE1LiBUaGVzZSBhcmUgYWxsIGNvbmplY3R1cmFsLCBhbmQgaXQgbWF5IHNlcnZlIGFzIGFuIGludGVyZXN0aW5nIGF2ZW51ZSBmb3IgZnVydGhlciBleHBsb3JhdGlvbi4gSG93ZXZlciB0aGlzIGlzIGJleW9uZCB0aGUgc2NvcGUgb2Ygb3VyIHByb2plY3QuCgpXZSBwb3N0dWxhdGUgdGhhdCB0aGUgcG9wdWxhcml0eSBvZiBjZXJ0YWluIHN0YXRpb25zIGFjcm9zcyB0aW1lIGlzIGR1ZSB0byB0aGUgZmFjdCB0aGV5IGFyZSBpbiBhcmVhcyBvZiBoZWF2eSBwZWRlc3RyaWFuIHRyYWZmaWMgKGUuZy4gY2l0eSBjZW50ZXJzKSwgYW5kIHRoZXkgYXJlIGluIGNsb3NlIHByb3hpbWl0eSB0byBvbmUgYW5vdGhlciBidXQgb3V0c2lkZSBjb21mb3J0YWJsZSB3YWxraW5nIHJhbmdlLiBXZSB0aHVzIHB1dCB0aGlzIGlkZWEgdG8gdGhlIHRlc3QgYnkgY29uc2lkZXJpbmcgc3BhdGlhbCBwYXR0ZXJucyBhbmQgZGlzdGFuY2VzIGFtb25nc3QgdGhlc2UgbW9yZSBwb3B1bGFyIHN0YXRpb25zLgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMgUG9wdWxhciBzdGF0aW9ucyAtIG1hcHMKcnNfZmlyc3RfcmV0dXJuJHR5cGUgPC0gIlJldHVybiIKcnNfbGFzdF9yZXR1cm4kdHlwZSA8LSAiUmV0dXJuIgpyc19maXJzdF9yZW50YWwkdHlwZSA8LSAiUmVudGFsIgpyc19sYXN0X3JlbnRhbCR0eXBlIDwtICJSZW50YWwiCgpRMlkxNSA8LSByYmluZChyc19maXJzdF9yZXR1cm4sIHJzX2ZpcnN0X3JlbnRhbCkKUTRZMTYgPC0gcmJpbmQocnNfbGFzdF9yZXR1cm4sIHJzX2xhc3RfcmVudGFsKQoKcGFsIDwtIGNvbG9yRmFjdG9yKGMoIm5hdnkiLCAicmVkIiksIGRvbWFpbiA9IGMoIlJldHVybiIsICJSZW50YWwiKSkKCiMgUHVycGxlOiBwb3B1bGFyIGZvciBib3RoIHJldHVybiBhbmQgcmVudGFsCiMgUmVkOiBvbmx5IGZvciByZW50YWwKIyBCbHVlOiBvbmx5IGZvciByZXR1cm4KIyBTaXplOiB0b3RhbCBmcmVxdWVuY3kgd2l0aGluIHRoZSBxdWFydGVyCgojIGZvciBRMiBvZiAyMDE1CmxlYWZsZXQoUTJZMTUpICU+JQogIGFkZFRpbGVzKCkgJT4lCiAgYWRkUHJvdmlkZXJUaWxlcygnQ2FydG9EQi5Qb3NpdHJvbicpICU+JQogIHNldFZpZXcobG5nPS03NS4xNjM3NjMsIGxhdD0zOS45NTI1NjgsIHpvb20gPSAxMykgJT4lCiAgYWRkQ2lyY2xlTWFya2VycygKICAgIHJhZGl1cyA9IH5zcXJ0KGNvdW50KS8xMCwKICAgIGNvbG9yID0gfnBhbCh0eXBlKSwKICAgIHN0cm9rZSA9IEZBTFNFLCBmaWxsT3BhY2l0eSA9IDAuNSwgCiAgICBwb3B1cCA9IH5uYW1lCiAgKQoKIyBmb3IgUTQgb2YgMjAxNgpsZWFmbGV0KFE0WTE2KSAlPiUKICBhZGRUaWxlcygpICU+JQogIGFkZFByb3ZpZGVyVGlsZXMoJ0NhcnRvREIuUG9zaXRyb24nKSAlPiUKICBzZXRWaWV3KGxuZz0tNzUuMTYzNzYzLCBsYXQ9MzkuOTUyNTY4LCB6b29tID0gMTMpICU+JQogIGFkZENpcmNsZU1hcmtlcnMoCiAgICByYWRpdXMgPSB+c3FydChjb3VudCkvMTAsCiAgICBjb2xvciA9IH5wYWwodHlwZSksCiAgICBzdHJva2UgPSBGQUxTRSwgZmlsbE9wYWNpdHkgPSAwLjUsIAogICAgcG9wdXAgPSB+bmFtZQogICkKYGBgCgo8YnI+IEluIHRoZSBhYm92ZSB0d28gaW50ZXJhY3RpdmUgbWFwcywgdGhlIGNvbG9yIHB1cnBsZSByZXByZXNlbnRzIHBvcHVsYXIgcmVudGFsIGFuZCByZXR1cm4gc3RhdGlvbnMsIHJlZCBmb3IgcmVudGFsLCBhbmQgYmx1ZSBmb3IgcmV0dXJuLiBUaGUgc2l6ZSBvZiB0aGUgZG90IGRlbm90ZXMgdGhlIHRvdGFsIGZyZXF1ZW5jeSBvZiByaWRlcyB3aXRoaW4gdGhlIHF1YXJ0ZXIgKHRvcDogUTIgb2YgMjAxNSwgYm90dG9tOiBib3R0b206IFE0IG9mIDIwMTYpLiBDcnVkZWx5IGluIGxpbmUgd2l0aCBvdXIgaHlwb3RoZXNpcywgbW9zdCBvZiB0aGUgbW9zdCBwb3B1bGFyIHN0YXRpb25zIChieSBkb3Qgc2l6ZSkgYXJlIGNvbmNlbnRyYXRlZCBpbiB0aGUgZXBpY2VudGVyIG9mIFBoaWxhZGVscGhpYSwgd2hpY2ggaXMgcm91Z2hseSBiZXR3ZWVuIFJpdHRlbmhvdXNlIFNxdWFyZSBhbmQgQ2hpbmF0b3duLiBXZSBhbHNvIHNlZSB0aGF0IFBNT0EgaXMgb2ZmIGluIHRoZSBkaXN0YW5jZSB0byB0aGUgbm9ydGhlYXN0LCBidXQgd2FzIGEgcG9wdWxhciByZW50YWwgYW5kIHJldHVybiBzdGF0aW9uIGluIFEyIG9mIDIwMTUuIFRoaXMgYWxzbyBjb3JyZXNwb25kcyB0byBvdXIgdGhlb3J5IHRoYXQgc3VtbWVyIG1hZGUgdGhpcyBhIHBvcHVsYXIgaHViIGZvciBiaWtlIHVzZXJzLiBJbiBRNCBvZiAyMDE2LCB0aGUgc2hpZnQgaW4gY2x1c3RlcmluZyBvZiBwb3B1bGFyIHN0YXRpb25zIHJldm9sdmVzIGFyb3VuZCB0aGUgdHdvIHNob3JlcyBvZiBTY2h1eWxraWxsIFJpdmVyLiBUaGlzIG1heSBjb3Jyb2JvcmF0ZSBvdXIgb2JzZXJ2YXRpb24gdGhhdCBwZW9wbGUgdXNlIGl0IGFzIGEgbWVhbnMgdG8gY3Jvc3MgdGhlIHJpdmVyIGZvciByb3V0aW5lIGNvbW11dGVzIChlLmcuIGJldHdlZW4gVW5pdmVyc2l0eSBDaXR5L1Bvd2VsdG9uIFZpbGxhZ2UgYW5kIFJpdHRlbmhvdXNlIFNxdWFyZSkgdGhhdCBhcmUgb3V0IG9mIHdhbGtpbmcgcmFuZ2UgYnV0IGNsb3NlIGVub3VnaCBmb3IgYSBiaWtlIHJpZGUuIFRoaXMgaXMgcHVyZWx5IHNwZWN1bGF0aXZlIGJ1dCBtYWtlcyBmb3IgYW4gaW50ZXJlc3RpbmcgaWRlYSB0byBmdXJ0aGVyIGludmVzdGlnYXRlLiBXZSB3aWxsIHRvdWNoIG9uIHRoaXMgYSBiaXQgbW9yZSBpbiB0aGUgbmV4dCBzZWN0aW9uLgoKIyMjIyBEYWlseSB0cmFmZmljCgpXZSBwdXQgdGhlIHRoZW9yeSBvZiB1c2luZyBJbmRlZ28gYXMgYSBmb3JtIG9mIGNvbW11dGUgdG8gdGhlIHRlc3QsIHVzaW5nIGRhaWx5IHRyYWZmaWMgYXMgYSBwcm94eSBmb3IgbWVhc3VyZW1lbnRzLiBXZSBwaWNrZWQgUTIgb2YgMjAxNiwgd2hpY2ggaXMgdGhlIHF1YXJ0ZXIgd2l0aCB0aGUgc2Vjb25kIG1vc3QgYmlrZSByaWRlcy4gV2UgaWRlYWxseSB3b3VsZCBoYXZlIGxpa2VkIHRvIGNob3NlbiBRMyBvZiAyMDE2IChtb3N0IHJlY2VudCBzdW1tZXIgcXVhcnRlciksIHdoaWNoIGlzIHRoZSBtb3N0IHBvcHVsYXIgcXVhcnRlciwgYnV0IHRoaXMgdGltZSBwZXJpb2Qgb3ZlcmxhcHMgd2l0aCBzdW1tZXIgdmFjYXRpb24gZm9yIG1hbnkgc3R1ZGVudHMuIFNpbmNlIHdlIGRpZCBub3Qgd2FudCB0byBpZ25vcmUgdGhpcyBwYXJ0aWN1bGFyIGRlbW9ncmFwaGljIHdoZW4gYW5hbHl6aW5nIHRoZSB1c2Ugb2YgYmlrZSB0cmlwcyBhcyBhIGZvcm0gb2YgY29tbXV0ZSwgd2UgdGh1cyBzZXR0bGVkIG9uIFEyIG9mIDIwMTYuIFdlIGNvbnNpZGVyZWQgYXZlcmFnZSBob3VybHkgdXNlcyBvZiBJbmRlZ28gYmlrZXMgcGVyIGRheSBmb3IgZXZlcnkgZGF5IG9mIFEyIG9mIDIwMTYuIFdlIHRoZW4gcGxvdHRlZCB0aGlzIG9uIGEgMjQtaG91ciB0aW1lLXNjYWxlLCB3aGljaCBpcyBzaG93biBiZWxvdy4gQXMgaWxsdXN0cmF0ZWQgYnkgdGhlIHBsb3QsIHRoZXJlIGFyZSBkcmFzdGljIGRpZmZlcmVuY2VzIGJldHdlZW4gd2Vla2RheSBhbmQgd2Vla2VuZCB1c2FnZSBhY3Jvc3MgYW55IHN0YW5kYXJkIDI0LWhvdXIgd2luZG93LiBPbiB3ZWVrZGF5cywgcGVhayBwZXJpb2RzIG9mIGJpa2UgdHJpcHMgd2VyZSBhcm91bmQgODowMCBBTSBhbmQgNTowMCBQTSwgd2hpY2ggY29ycmVzcG9uZCB0byB0aGUgdGltZSBmcmFtZXMgdGhhdCBtb3N0IGluZGl2aWR1YWxzIGNvbW11dGUgcmVzcGVjdGl2ZWx5IHRvIGFuZCBmcm9tIHdvcmsgYW5kIHNjaG9vbC4gT24gd2Vla2VuZHMsIGhvd2V2ZXIsIHBlYWsgdXNhZ2Ugb2NjdXJlZCBkdXJpbmcgdGhlIGFmdGVybm9vbi4gTW9yZW92ZXIsIG1vcmUgYmlrZSB0cmlwcyB3ZXJlIG9ic2VydmVkIG9uIHdlZWtkYXlzIGZvciBhbG1vc3QgYW55IGdpdmVuIHRpbWUgaW50ZXJ2YWwgdGhhbiB0aGF0IG9mIHdlZWtlbmRzLiBUaHVzLCB0aGlzIG1heSBwb3RlbnRpYWxseSBkZW1vbnN0cmF0ZSB0aGF0IEluZGVnbyBoYXMgYmVjb21lIGEgbWVhbnMgZm9yIGV2ZXJ5ZGF5IGNvbW11dGUgaW4gUGhpbGFkZWxwaGlhLiAKCmBgYHtyfQojIDIuIFBlYWsgUGVyaW9kcyAoZ3JvdXAgYnkgaG91ciwgd2Vla2RheXMvd2Vla2VuZHMsIHNlYXNvbikKIyAyLjEgV2Vla2RheXMvd2Vla2VuZHMgaGlzdG9ncmFtIG9mIHN0YXJ0IChyZW50YWwpIGhvdXJzIGZvciBhbGwgc3RhdGlvbnMgaW4gMjAxNlEyCmxpYnJhcnkobHVicmlkYXRlKQpRMlkxNiA8LSBkZltkZiR5ZWFyPT0yMDE2ICYgZGYkbW9udGggJWluJSBjKCIwNCIsIjA1IiwiMDYiKSwgXQpRMlkxNiRkYXlfdHlwZSA8LSBjaHJvbjo6aXMud2Vla2VuZChRMlkxNiRkYXRlKQpRMlkxNiRkYXlfdHlwZSA8LSBpZmVsc2UoUTJZMTYkZGF5X3R5cGUgPT0gMSwgIldlZWtlbmRzIiwgIldlZWtkYXlzIikKbl93a25kcyA8LSBsZW5ndGgodW5pcXVlKFEyWTE2W1EyWTE2JGRheV90eXBlPT0iV2Vla2VuZHMiLDE0XSkpCm5fd2tkeXMgPC0gbGVuZ3RoKHVuaXF1ZShRMlkxNltRMlkxNiRkYXlfdHlwZT09IldlZWtkYXlzIiwxNF0pKQp3a25kc19wZWFrIDwtIFEyWTE2W1EyWTE2JGRheV90eXBlPT0iV2Vla2VuZHMiLF0gJT4lIGdyb3VwX2J5KHNfaG91cixkYXlfdHlwZSkgJT4lIHN1bW1hcml6ZShyZW50YWxfZnJlcSA9IG4oKS9uX3drbmRzKQp3a2R5c19wZWFrIDwtIFEyWTE2W1EyWTE2JGRheV90eXBlPT0iV2Vla2RheXMiLF0gJT4lIGdyb3VwX2J5KHNfaG91cixkYXlfdHlwZSkgJT4lIHN1bW1hcml6ZShyZW50YWxfZnJlcSA9IG4oKS9uX3drZHlzKQpwZWFrIDwtIHJiaW5kKHdrbmRzX3BlYWssIHdrZHlzX3BlYWspCnJtKHdrbmRzX3BlYWssIHdrZHlzX3BlYWspCnBlYWskc19ob3VyIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHBlYWskc19ob3VyKSkKZm9vMSA8LSBjKDAsNCw4LDEyLDE2LDIwKQpmb28yIDwtIGMoIjEyYW0iLCI0YW0iLCI4YW0iLCIxMnBtIiwiNHBtIiwiOHBtIikKYGBgCgoKYGBge3IgZmlnLndpZHRoPTMsIGZpZy5oZWlnaHQ9M30KZ2dwbG90KHBlYWssIGFlcyhzX2hvdXIsIHJlbnRhbF9mcmVxKSkgKwogIGdlb21fbGluZShhZXMoY29sb3I9ZGF5X3R5cGUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMS4xLCBhZXMoY29sb3I9ZGF5X3R5cGUpKSArCiAgbGFicyh0aXRsZT0iUGVhayBSZW50YWwgSG91cnMgMjAxNlEyIiwKICAgICAgIHN1YnRpdGxlPSJBdmVyYWdlIERhaWx5IFJlbnRhbCBGcmVxdWVuY3kgYnkgSG91ciIpICsKICBzY2FsZV9jb2xvcl9maXZldGhpcnR5ZWlnaHQoIiIpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwgImNtIikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gZm9vMSwgbGFiZWxzID0gZm9vMikKYGBgCgpXZSBub3cgZXh0ZW5kIG91ciBhbmFseXNpcyB0byBhbGwgcXVhcnRlcmx5IGRhdGEgYXZhaWxhYmxlLCB3aGljaCBpcyBzaG93biBpbiB0aGUgcGxvdCBpbW1lZGlhdGVseSBiZWxvdy4gTXVjaCBvZiB0aGUgc2FtZSB0cmVuZHMgZnJvbSBRMiBvZiAyMDE2IGNhbiBiZSBnZW5lcmFsaXplZCB0byBvdGhlciBxdWFydGVycy4gQXBhcnQgZnJvbSBhIGZldyBkYXRhIHBvaW50cywgdGhlIG51bWJlciBvZiBiaWtlIHRyaXBzIG9uIHdlZWtkYXlzIGFyZSBhbG1vc3QgYWx3YXlzIGxhcmdlciB0aGFuIHRoYXQgb2Ygd2Vla2VuZHMuIEhvd2V2ZXIsIHdlIGFsc28gb2JzZXJ2ZSB0aGUgYWZvcmVtZW50aW9uZWQgc2Vhc29uYWxpdHkgb2YgYmlrZSB0cmlwcywgaW4gd2hpY2ggbW9yZSBiaWtlIHRyaXBzIGFyZSBvYnNlcnZlZCBpbiB0aGUgc3VtbWVyIHRoYW4gaW4gdGhlIHdpbnRlciBtb250aHMuIFRoaXMgaXMgZXNwZWNpYWxseSB0cnVlIGZvciB0cmlwcyBvbiB3ZWVrZW5kIChlLmcuIHVzYWdlIG9uIHdlZWtlbmRzIGluIFNlcHQgb2YgMjAxNSBpcyBieSBmYXIgdGhlIGxhcmdlc3QpLiBUaGVyZWZvcmUsIHVzZSBvZiBJbmRlZ28gYXMgbGVpc3VyZSBkdXJpbmcgc3VtbWVyIHdlZWtlbmRzIG1heSBiZSB0aGUgY2F1c2FsIHZhcmlhYmxlIG9mIHRoZSBwZWFrcyB3ZSBvYnNlcnZlLiAKCmBgYHtyfQojIDIuMiBNb250aCBhZ2dyZWdhdGlvbiBvZiByZXN1bHRzCnF1YXJ0ZXJfYWdnciA8LSBkZgojcXVhcnRlcl9hZ2dyJHF1YXJ0ZXIgPC0gKGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHF1YXJ0ZXJfYWdnciRtb250aCkpIC0gMSkgJS8lIDMgKyAxCnF1YXJ0ZXJfYWdnciRkYXlfdHlwZSA8LSBjaHJvbjo6aXMud2Vla2VuZChxdWFydGVyX2FnZ3IkZGF0ZSkKcXVhcnRlcl9hZ2dyJGRheV90eXBlIDwtIGlmZWxzZShxdWFydGVyX2FnZ3IkZGF5X3R5cGUgPT0gMSwgIldlZWtlbmRzIiwgIldlZWtkYXlzIikKcXVhcnRlcl9hZ2dyIDwtIHF1YXJ0ZXJfYWdnciAlPiUgc2VsZWN0KHllYXIsIG1vbnRoLCBkYXRlLCBkYXlfdHlwZSkKcXVhcnRlcl9hZ2dyIDwtIHF1YXJ0ZXJfYWdnciAlPiUgZ3JvdXBfYnkoZGF0ZSwgeWVhciwgbW9udGgsIGRheV90eXBlKSAlPiUgc3VtbWFyaXNlKGNvdW50PW4oKSkKcXVhcnRlcl9hZ2dyIDwtIHF1YXJ0ZXJfYWdnciAlPiUgZ3JvdXBfYnkoeWVhciwgbW9udGgsIGRheV90eXBlKSAlPiUgc3VtbWFyaXNlKGF2ZXJhZ2U9bWVhbihjb3VudCkpCnF1YXJ0ZXJfYWdnciRubyA8LSAoMDoobnJvdyhxdWFydGVyX2FnZ3IpLTEpKSAlLyUgMiArIDEKZm9vMSA8LSBzZXEoMSwyMSxieT0zKQpmb28yIDwtIGMoIkFwciAyMDE1IiwiSnVsIDIwMTUiLCJPY3QgMjAxNSIsICJKYW4gMjAxNiIsICJBcHIgMjAxNiIsIkp1bCAyMDE2IiwiT2N0IDIwMTYiKQpgYGAKCmBgYHtyIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTN9CmdncGxvdChxdWFydGVyX2FnZ3IsIGFlcyhubywgYXZlcmFnZSkpICsKICBnZW9tX2xpbmUoYWVzKGNvbG9yPWRheV90eXBlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuMSwgYWVzKGNvbG9yPWRheV90eXBlKSkgKwogIGxhYnModGl0bGU9Ik1vbnRobHkgUmVudGFsIFBhdHRlcm4iLAogICAgICAgc3VidGl0bGU9IkRhaWx5IEF2ZXJhZ2Ugb2YgUmVudGFsIEZyZXF1ZW5jaWVzIikgKwogIHNjYWxlX2NvbG9yX2ZpdmV0aGlydHllaWdodCgiIikgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjUsMC41LDAuNSwwLjUpLCAiY20iKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBmb28xLCBsYWJlbHMgPSBmb28yKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMCwgaGp1c3QgPSAxKSkKYGBgCgoKSXQgaXMgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCBvbmUgb2YgdGhlIG1vcmUgZ2xhcmluZyBkZWZpY2llbmNpZXMgb2Ygb3VyIHJhdyBkYXRhc2V0IGlzIHRoZSBsYWNrIG9mIGhpc3RvcmljYWwgZGF0YTsgd2Ugb25seSBoYXZlIGRhdGEgZnJvbSBBcHJpbCAyMDE1IG9ud2FyZHMuIEl0IHRodXMgbWFrZXMgaXQgbW9yZSBkaWZmaWN1bHQgdG8gZHJhdyBjb25jbHVzaXZlIHJlbWFrcnMgcmVnYXJkaW5nIHllYXJseSBvciBldmVuIHNlYXNvbmFsIHBhdHRlcm5zIHRoYXQgcmVxdWlyZSBkYXRhIG92ZXIgbG9uZ2VyIHRpbWUgcGVyaW9kcy4gTm9uZXRoZWxlc3MsIHNldmVyYWwgaW1wb3J0YW50IHRyZW5kcyBjYW4gc3RpbGwgYmUgb2JzZXJ2ZWQgb3ZlciB0aGUgdGltZSBwZXJpb2QgZm9yIHdoaWNoIHRoZSBkYXRhc2V0IGlzIGF2YWlsYWJsZS4KCkZvciBpbnN0YW5jZSwgbGV0IHVzIHRha2UgYSBsb29rIGF0IG1vbnRobHkgYmlrZSB0cmlwcyBhcyBkZXRlcm1pbmVkIGZyb20gdGhlIG51bWJlciBvZiBhdmVyYWdlIHRyaXBzIHBlciBkYXksIHNob3duIGJlbG93LiBTdWNoIGFuIGFnZ3JlZ2F0aW9uLCBhcyBvcHBvc2VkIHRvIHRha2luZyB0aGUgbW9udGhseSBhdmVyYWdlIGFzIGlzLCB3YXMgZG9uZSB0byBzbW9vdGggb3V0IGRhaWx5IGZsdWN0dWF0aW9ucyB0aGF0IGNhbiBiZSBjb25zaWRlcmVkIG5vaXNlIGFzIHdlIG1ha2Ugb3VyIHRpbWUgc2NhbGUgbW9yZSBncmFudWxhci4gV2Ugb2JzZXJ2ZSB0d28gbWFpbiB0aGVtZXMuIEZpcnN0LCB0aGVyZSBzZWVtcyB0byBiZSBwYXJ0aWN1bGFyIG1vbnRocyBpbiB3aGljaCBiaWtlIHRyaXBzIGFyZSBtb3JlIGFuZCBsZXNzIGZyZXF1ZW50LiBBY3Jvc3MgYW55IGdpdmVuIGNhbGVuZGFyIHllYXIsIHRoZXJlIGFyZSBtb3JlIGJpa2UgcmlkZXMgdGhyb3VnaCB0aGUgc3VtbWVyLCB1bHRpbWF0ZWx5IHBlYWtpbmcgaW4gU2VwdGVtYmVyLiBCaWtlIHRyaXBzIGRlY2xpbmUgdGhlcmVhZnRlciwgaGl0dGluZyBsb3cgcG9pbnRzIGluIERlY2VtYmVyL0phbnVhcnkuIFRoaXMgaXMgaW50dWl0aXZlIGFzIGdvb2Qgd2VhdGhlciBpcyBjb3JyZWxhdGVkIHdpdGggaG93IHdpbGxpbmcgYW5kIHByYWN0aWNhbCBiaWtlIHJpZGVzIGZ1bmN0aW9uIGFzIGEgbWVhbnMgb2YgdHJhbnNwb3J0YXRpb24uIFRodXMsIHdpdGggYWJ5c21hbCB3ZWF0aGVyIGNvbmRpdGlvbnMgaW4gdGhlIHdpbnRlciwgcGVvcGxlIGFyZSBwcm9iYWJseSBsZXNzIHdpbGxpbmcgdG8gdXNlIEluZGVnbyBiaWtlcy4gU2Vjb25kLCB3ZSBvYnNlcnZlIEluZGVnbydzIHBvcHVsYXJpdHkgaW5jcmVhc2UgdGhyb3VnaCB0aW1lIGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgYmlrZSB0cmlwcyBpbiBhIGdpdmVuIG1vbnRoIGFzIGNvbXBhcmVkIHRvIHRoZSBzYW1lIG1vbnRoIGluIGEgcHJlY2VkaW5nIHllYXIuIEluIFNlcHQgMjAxNiwgdXNhZ2Ugd2FzIGF0IGFuIGFsbC10aW1lIGhpZ2gsIHdpdGggbW9yZSB0aGFuIDI2MDAgcmlkZXMgcGVyIGRheSAoY2YuIGFwcHJveGltYXRlbHkgMjI1MCByaWRlcyBpbiBTZXB0IDIwMTYpLiBJbiBEZWMgMjAxNiwgYmlrZSByaWRlcyB3ZXJlIHJvdWdobHkgYSBsaXR0bGUgaGlnaGVyIHRoYW4gMTAwMCByaWRlcyBwZXIgZGF5IChjZi4gNjAwIGluIERlYyAyMDE1KS4gSXQgc2hvdWxkIGJlIG5vdGVkIHRoYXQgY29uZm91bmRpbmcgZmFjdG9ycyBzdWNoIGFzIHdlYXRoZXIgY29uZGl0aW9ucyBpbiBhIGdpdmVuIHllYXIgKGUuZy4gRWwgTmlubykgYW5kIHRoZSBpbnN1ZmZpY2llbnQgaGlzdG9yaWNhbCBkYXRhIHdlIGhhdmUgb24taGFuZCBtYXkgc29tZXdoYXQgcmVuZGVyIG91ciByZXN1bHRzIHF1ZXN0aW9uYWJsZS4gV2UsIGhvd2V2ZXIsIGJlbGlldmUgdGhpcyBpcyBub3QgdGhlIGNhc2Ugc2luY2UgbW9yZSBlY29mcmllbmRseSBtZWFucyBvZiB0cmFuc3BvcnRhdGlvbiBzaG91bGQgdGhlb3JldGljYWxseSBiZWNvbWUgbW9yZSBwb3B1bGFyIGR1ZSB0byBncm93aW5nIGVudmlyb25tZW50YWwgc2VudGltZW50cyBpbiB0aGUgbWFzcyBwb3B1bGF0aW9uLgoKYGBge3IgcmVzdWx0cz0iaGlkZSIsZmlnLndpZHRoPTMsIGZpZy5oZWlnaHQ9M30KIyBNb250aGx5IEJpa2UgVHJpcHMKZGZfY291bnRfYnlfbW9udGggPC0gZGYgJT4lIGdyb3VwX2J5KHllYXIsIG1vbnRoKSAlPiUgc3VtbWFyaXplKGNvdW50ID0gbigpKQpkZl9hdmVyYWdlX2J5X21vbnRoIDwtIGRmICU+JSBncm91cF9ieSh5ZWFyLCBtb250aCwgZGF5KSAlPiUgc3VtbWFyaXplKGNvdW50ID0gbigpKQpkZl9hdmVyYWdlX2J5X21vbnRoIDwtIGRmX2F2ZXJhZ2VfYnlfbW9udGggJT4lIGdyb3VwX2J5KHllYXIsIG1vbnRoKSAlPiUgc3VtbWFyaXplKGF2ZyA9IG1lYW4oY291bnQpKQpkZl9jb3VudF9ieV9tb250aCRkYXRlIDwtIHpvbzo6YXMueWVhcm1vbihwYXN0ZShkZl9jb3VudF9ieV9tb250aCR5ZWFyLCBkZl9jb3VudF9ieV9tb250aCRtb250aCksICIlWSAlbSIpCmRmX2NvdW50X2J5X21vbnRoJGF2ZXJhZ2UgPC0gZGZfYXZlcmFnZV9ieV9tb250aCRhdmcKZGZfY291bnRfYnlfbW9udGgkbm8gPC0gMTpucm93KGRmX2NvdW50X2J5X21vbnRoKQpkZl9jb3VudF9ieV9tb250aCA8LSBkZl9jb3VudF9ieV9tb250aFssLWMoMSwyKV0Kcm0oZGZfYXZlcmFnZV9ieV9tb250aCkKZGZfY291bnRfYnlfbW9udGggPC0gdGlkeXI6OmdhdGhlcihkZl9jb3VudF9ieV9tb250aCwgdHlwZSwgdmFsdWUsIGMoMSwzKSkKCmZvbzEgPC0gYygwLCA1MDAsIDEwMDAsIDE1MDAsIDIwMDAsIDI1MDAsIDMwMDApCmZvbzIgPC0gYygzLCA2LCA5LCAxMiwgMTUsIDE4LCAyMSkKCmdncGxvdCgpICsKICBnZW9tX2xpbmUoYWVzKG5vLCB2YWx1ZSwgY29sb3I9ImJsdWUiKSwKICAgICAgICAgICAgZGZfY291bnRfYnlfbW9udGhbZGZfY291bnRfYnlfbW9udGgkdHlwZT09ImF2ZXJhZ2UiLF0pICsKICBnZW9tX3BvaW50KHNpemUgPSAxLjEpICsKICBnZ3RpdGxlKCJNb250aGx5IEluZGVnbyBCaWtlIFRyaXBzXG4oQXZlcmFnZSBwZXIgZGF5KSIpICsKICB4bGFiKCJNb250aCIpICsKICB5bGFiKCJDb3VudCIpICsKICBzY2FsZV9jb2xvcl9maXZldGhpcnR5ZWlnaHQoImN5bCIpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSAzMCwgaGp1c3QgPSAxKSxsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMzAwMCksIGJyZWFrcyA9IGZvbzEpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gZm9vMiwgbGFiZWxzID0gZGZfY291bnRfYnlfbW9udGgkZGF0ZVtmb28yXSkgKwogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAuNSwwLjUsMC41LDAuNSksICJjbSIpKQpgYGAKCkFzIGFuIGludGVyZXN0aW5nIHRhbmdlbnQsIHdlIGV4YW1pbmUgdGhlIG1vc3QgcG9wdWxhciByb3V0ZXMgYmFzZWQgb24gYSBkYWlseSBhdmVyYWdlIG9mIHRyaXBzIGJldHdlZWVuIGFsbCBzdGF0aW9ucyBhY3Jvc3MgYXZhaWxhYmxlIHF1YXJ0ZXJzLiBXZSBzZWUgdGhhdCB0aGUgbW9zdCBwb3B1bGFyIHJvdXRlcyBmcmVxdWVudGVkIGJ5IG1vc3QgdXNlcnMgYXJlIHRob3NlIGJldHdlZW4gaHVicyAoaS5lLiBhbHJlYWR5IHBvcHVsYXIgc3RhdGlvbnMpLiBJbiB0aGUgaW50ZXJhY3RpdmUgbWFwIGJlbG93LCB3ZSBwbG90IHRoZSBFdWNsaWRlYW4gZGlzdGFuY2Ugb2YgdGhlIHRvcCBmaXZlIG1vc3QgcG9wdWxhciByb3V0ZXMuIEZvciBpbnN0YW5jZSwgb25lIHN1Y2ggcm91dGUgaXMgdGhhdCBiZXR3ZWVuIFVuaXZlcnNpdHkgU3RhdGlvbiBhbmQgMjNyZCBhbmQgU291dGggKGkuZS4gZG93bnRvd24pLiBJdCBpcyB3b3J0aCBub3RpbmcgdGhhdCBtb3N0IG9mIHRoZXNlIHJvdXRlcyBhcmUgZWl0aGVyIGFjcm9zcyB0aGUgZG93bnRvd24gYXJlYSBvciBhY3Jvc3MgdGhlIFNjaHV5bGtpbGwgUml2ZXIsIHdoaWNoIHNvbWV3aGF0IHN1YnN0YW50aWF0ZXMgdGhlIGFzc2VydGlvbiB0aGF0IHBvcHVsYXIgcm91dGVzIGFyZSBhIG1lYW5zIGZvciBjb21tdXRlLgoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMjIyMjIyMjIFBvcHVsYXIgUm91dGVzICMjIyMjIyMjIAojIENvbnRlbnQKIyBNb3N0IFBvcHVsYXIgMjAgUm91dGVzCiMgU2luZ2xlIGRvdHMgbWVhbnMgcmVudCBmcm9tIHRoZXNlIHN0YXRpb25zIGFuZCByZXR1cm4gdG8gdGhlc2Ugc3RhdGlvbnMgYXMgd2VsbApwb3B1bGFyX3JvdXRlIDwtIGRmICU+JSBncm91cF9ieShkYXRlLCB5ZWFyLCBtb250aCwgc3RhcnRfc3RhdGlvbl9pZCwgZW5kX3N0YXRpb25faWQpICU+JSBzdW1tYXJpc2UoY291bnQ9bigpKQpwb3B1bGFyX3JvdXRlIDwtIHBvcHVsYXJfcm91dGUgJT4lIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb25faWQsIGVuZF9zdGF0aW9uX2lkKSAlPiUgc3VtbWFyaXNlKGF2Zz1tZWFuKGNvdW50KSkgJT4lIG5hLm9taXQKcG9wdWxhcl9yb3V0ZSA8LSBwb3B1bGFyX3JvdXRlW29yZGVyKC1wb3B1bGFyX3JvdXRlJGF2ZyksXVsxOjIwLF0KIyBFeHRyYWN0IGFsbCBwb2ludHMKbG9jIDwtIGMocG9wdWxhcl9yb3V0ZSRzdGFydF9zdGF0aW9uX2lkLCBwb3B1bGFyX3JvdXRlJGVuZF9zdGF0aW9uX2lkKSAlPiUgdW5pcXVlICU+JSBhcy5pbnRlZ2VyCnN0YXRpb25fbG9jJGlkIDwtIGFzLm51bWVyaWMoc3RhdGlvbl9sb2MkaWQpCmRvdF9sb2MgPC0gZGF0YS5mcmFtZShpZD1sb2MpICU+JSBsZWZ0X2pvaW4oc3RhdGlvbl9sb2MsIGJ5PWMoImlkIj0iaWQiKSkgJT4lIG5hLm9taXQKCmwxIDwtIGxlYWZsZXQoZG90X2xvYykgJT4lCiAgYWRkVGlsZXMoKSAlPiUKICBhZGRQcm92aWRlclRpbGVzKCdDYXJ0b0RCLlBvc2l0cm9uJykgJT4lCiAgc2V0Vmlldyhsbmc9LTc1LjE2Mzc2MywgbGF0PTM5Ljk1MjU2OCwgem9vbSA9IDEzKSAlPiUKICBhZGRDaXJjbGVNYXJrZXJzKAogICAgY29sb3IgPSAibmF2eSIsCiAgICBzdHJva2UgPSBGQUxTRSwgZmlsbE9wYWNpdHkgPSAwLjUsIAogICAgcG9wdXAgPSB+bmFtZQogICkKcG9wdSA8LSBwb3B1bGFyX3JvdXRlW3BvcHVsYXJfcm91dGUkc3RhcnRfc3RhdGlvbl9pZCE9cG9wdWxhcl9yb3V0ZSRlbmRfc3RhdGlvbl9pZCxdCmRvdF9sb2MyIDwtIGRvdF9sb2MKY29sbmFtZXMocG9wdSlbMV0gPC0gImlkIgpwb3B1JGlkIDwtIGFzLm51bWVyaWMocG9wdSRpZCkKcG9wdSA8LSBwb3B1ICU+JSBsZWZ0X2pvaW4oZG90X2xvYzIsIGJ5PWMoImlkIj0iaWQiKSkgJT4lIG5hLm9taXQKY29sbmFtZXMocG9wdSkgPC0gYygic2lkIiwgImlkIiwgImF2ZyIsICJzX2xhdCIsICJzX2xvbmciLCAibmFtZSIsICJkYXRlIikKZG90X2xvYzIkaWQgPC0gYXMubnVtZXJpYyhkb3RfbG9jMiRpZCkKcG9wdSA8LSBwb3B1ICU+JSBsZWZ0X2pvaW4oZG90X2xvYzIsIGJ5PWMoImlkIj0iaWQiKSkgJT4lIG5hLm9taXQKY29sbmFtZXMocG9wdSkgPC0gYygic2lkIiwgImlkIiwgImF2ZyIsICJzX2xhdCIsICJzX2xvbmciLCAic19uYW1lIiwgImRhdGUiLCAiZV9sYXQiLCAiZV9sb25nIiwgImVfbmFtZSIsICJkYXRlX2UiKQpmb3IoaSBpbiAxOm5yb3cocG9wdSkpewogIGwxIDwtIGFkZFBvbHlsaW5lcyhsMSwgbGF0ID0gYXMubnVtZXJpYyhwb3B1W2ksIGMoNCwgOCldKSwgbG5nID0gYXMubnVtZXJpYyhwb3B1W2ksIGMoNSwgOSldKSkKfQpsMQpgYGAKCkJhc2VkIG9uIHRoZSBhYm92ZSBmaW5kaW5ncywgd2UgY2FuIHNheSB3aXRoIGEgY2VydGFpbiBsZXZlbCBvZiBjb25maWRlbmNlIHRoYXQgdGhlIHZvbHVtZSBvZiBiaWtlIHRyaXBzIGlzIGFuIGltcG9ydGFudCBpbmRpY2F0b3Igb2YgSW5kZWdvJ3MgdXRpbGl0eSByYXRlIG92ZXIgZGlmZmVyZW50IGNyb3NzLXNlY3Rpb25zIG9mIHRpbWUuIEhvd2V2ZXIsIGl0IGRvZXMgbGl0dGxlIHRvIHRlbGwgdXMgaG93IChub3QgaG93IG11Y2gpIEluZGVnbyBpcyBiZWluZyB1dGlsaXplZC4gV2l0aCB0aGlzIGluIG1pbmQsIHdlIG1vdGl2YXRlIG91ciBuZXh0IHBvcnRpb24gb2YgYW5hbHlzaXMuCgpTaW5jZSB0aGUgaW5oZXJlbnQgcHVycG9zZSBvZiBiaWtlIHNoYXJlIG5ldHdvcmtzIGFyZSB0byBtZWRpYXRlIHJlbGF0aXZlbHkgc2hvcnQgY29tbXV0ZXMsIHdlIHdhbnQgdG8gZXhhbWluZSB0aGUgbGVuZ3RoIG9mIGVhY2ggdHJpcC4gV2Ugc3VybWlzZSB0aGF0IHNob3J0ZXIgdHJpcHMgKDAtMzAgbWludXRlcykgc2hvdWxkIGJlIHRoZSBpZGVhbCByYW5nZSBzaW5jZSBhbnl0aGluZyBiZXlvbmQgdGhhdCBpcyBzaW1wbHkgZXhlcmNpc2UgYW5kIG90aGVyIHRyYW5zcG9ydGF0aW9uIG1ldGhvZHMgYmVjb21lIGluY3JlYXNpbmdseSBtb3JlIGFwcGVhbGluZy4gV2UgY29uc2VxdWVudGx5IGV4cGxvcmUgZmVhdHVyZXMgcmVsYXRlZCB0byB0aGUgbGVuZ3RoIG9mIGJpa2UgcmlkZXMgbmV4dC4KCiMjIyMgVHJpcCBkdXJhdGlvbnMKCkluIHRoaXMgc2VjdGlvbiwgd2UgdHJ5IHRvIGVsdWNpZGF0ZSB0aGUgbW9yZSBzYWxpZW50IHBhdHRlcm5zIHBlcnRhaW5pbmcgdG8gdHJpcCBkdXJhdGlvbnMuIEZyb20gdGhlIHR3byBoaXN0b2dyYW1zIGJlbG93LCB3aGljaCBpbGx1c3RyYXRlIHRoZSBkdXJhdGlvbnMgb2YgYWxsIHRyaXBzIGluIFEyIGFuZCBRNCBvZiAyMDE2IGFjcm9zcyB3ZWVrZGF5cyBhbmQgd2Vla2VuZHMsIHdlIG9ic2VydmUgdGhhdCBtb3N0IHRyaXBzIHRha2VuIHdlcmUgdW5kZXIgMTUgbWludXRlcy4gVGhlc2UgdHdvIHBhcnRpY3VsYXIgcXVhcnRlcnMgd2VyZSBjaG9zZW4gc2luY2UgdGhleSBhcmUgdGhlIGxhdGVzdCBxdWFydGVycyB0aGF0IGFsc28gb2ZmZXJzIGEgY29udHJhc3QgaW4gc2Vhc29uYWxpdHkuIER1cmluZyB3ZWVrZGF5cyBmb3IgYm90aCBxdWFydGVycywgc2hvcnRlciBjb21tdXRlcyAoPCAxNSBtaW51dGVzKSB3ZXJlIHRoZSBvdmVyd2hlbG1pbmcgbWFqb3JpdHkgb2YgdHJpcCBkdXJhdGlvbnMuIFRoZXkgbWFrZSB1cCBtb3JlIHRoYW4gNzAlIG9mIHRoZSB0b3RhbCBjb3VudC4gVGhlIHNhbWUgaXMgdHJ1ZSBmb3Igd2Vla2VuZHMsIGJ1dCB0byBhIGxlc3NlciBleHRlbnQuIFRoaXMgbWF5IG9uY2UgYWdhaW4gYmUgYW4gaW5kaWNhdG9yIHRoYXQgYmlrZXMgdXNlZCBkdXJpbmcgd2Vla2RheXMgYXJlIGZvciBjb21tdXRlIChlLmcuIHRvIGFuZCBmcm9tIHdvcmsgYW5kIHNjaG9vbCkgc28gdGhleSBhcmUgaW5oZXJlbnRseSBzaG9ydGVyLCB3aGVyZWFzIGJpa2VzIGFyZSB1c2VkIGZvciBtb3JlIHJlY3JlYXRpb25hbCBwdXJwb3NlcyBvbiB3ZWVrZW5kcyBzbyB0aGVpciB0aW1lIG9mIHVzZSBpcyBuYXR1cmFsbHkgbG9uZ2VyLiBUaGUgbGF0dGVyIGlzIGNvcnJvYm9yYXRlZCBieSB0aGUgZmFjdCB0aGF0IGR1cmluZyBzdW1tZXIgd2Vla2VuZHMsIHRoZSBmcmVxdWVuY3kgb2YgbG9uZ2VyIHRyaXBzIG91dHdlaWdoZWQgdGhhdCBvZiBzaG9ydGVyIHRyaXBzLgoKSWYgd2UgaWdub3JlIGFueSBkaWZmZXJlbnRpYXRpb24gb2YgdHJpcCBkdXJhdGlvbnMgYmV0d2VlbiB3ZWVrZGF5cyBhbmQgd2Vla2VuZHMsIHdlIG9ic2VydmUgbXVjaCBvZiB0aGUgc2FtZSB0cmVuZHMuIEZvciBRNCBvZiAyMDE2LCB0aGUgbGF0ZXN0IHF1YXJ0ZXJseSBkYXRhIGF2YWlsYWJsZSB0byB1cywgdGhlIG1vc3QgcHJldmFsZW50IGR1cmF0aW9uIG9mIGJpa2UgcmlkZXMgbGllIGJldHdlZW4gNSBhbmQgMTAgbWludXRlcy4gSW4gZmFjdCwgdGhlIG92ZXJ3aGVsbWluZyBtYWpvcml0eSBvZiB0cmlwcyBhcmUgbGVzcyB0aGFuIDMwIG1pbnV0ZXMuIE9uIHRoZSBjb250cmFyeSwgd2Ugc2VlIHRoYXQgbG9uZyB0cmlwcyBhcmUgYWN0dWFsbHkgb3V0bGllcnMgaW4gb3VyIGRhdGFzZXQuIFRyaXBzIGJldHdlZW4gdHdvIGFuZCBmb3VyIGhvdXJzIG9ubHkgbWFrZSB1cCAwLjklIG9mIGFsbCByaWRlcyBpbiBRNCBvZiAyMDE2LCBhbmQgdHJpcHMgZXhjZWVkaW5nIDQgaG91cnMgbWFrZSB1cCAwLjclIG9mIHRoZSB0b3RhbC4KCmBgYHtyfQojIDEuIEdlbmVyYWwgUGF0dGVybiBWaXN1YWxpemF0aW9uCiMgMS4xIFRyaXAgRHVyYXRpb24gRGlzdHJpYnV0aW9ucyAoZGltZW5zaW9uIG9mIHRpbWUpCiMgV2Vla2RheXMvd2Vla2VuZHMgaW4gMjAxNlEyIGFuZCAyMDE2UTQKbGlicmFyeShnZ3RoZW1lcykKUTJZMTYgPC0gZGZbZGYkeWVhcj09MjAxNiAmIGRmJG1vbnRoICVpbiUgYygiMDQiLCIwNSIsIjA2IiksIF0KUTJZMTYkZGF5X3R5cGUgPC0gY2hyb246OmlzLndlZWtlbmQoUTJZMTYkZGF0ZSkKUTJZMTYkZGF5X3R5cGUgPC0gaWZlbHNlKFEyWTE2JGRheV90eXBlID09IDEsICJXZWVrZW5kcyIsICJXZWVrZGF5cyIpClE0WTE2IDwtIGRmW2RmJHllYXI9PTIwMTYgJiBkZiRtb250aCAlaW4lIGMoIjEwIiwiMTEiLCIxMiIpLCBdClE0WTE2JGRheV90eXBlIDwtIGNocm9uOjppcy53ZWVrZW5kKFE0WTE2JGRhdGUpClE0WTE2JGRheV90eXBlIDwtIGlmZWxzZShRNFkxNiRkYXlfdHlwZSA9PSAxLCAiV2Vla2VuZHMiLCAiV2Vla2RheXMiKQpRMlkxNiRkdXJhdGlvbl9jdXQgPC0gY3V0KGFzLm51bWVyaWMoUTJZMTYkZHVyYXRpb24pLCBjKDAsNSwxMCwxNSwyMCwzMCw2MCwxMjAsMjQwLDE0NDApKQpRNFkxNiRkdXJhdGlvbl9jdXQgPC0gY3V0KGFzLm51bWVyaWMoUTRZMTYkZHVyYXRpb24pLCBjKDAsNSwxMCwxNSwyMCwzMCw2MCwxMjAsMjQwLDE0NDApKQpRMlkxNl9kdXIgPC0gUTJZMTYgJT4lIGdyb3VwX2J5KGRheV90eXBlLCBkdXJhdGlvbl9jdXQsIGRhdGUpICU+JSBzdW1tYXJpc2UoY291bnQ9bigpKQpRMlkxNl9kdXI8LSBRMlkxNl9kdXIgJT4lIGdyb3VwX2J5KGRheV90eXBlLCBkdXJhdGlvbl9jdXQpICU+JSBzdW1tYXJpc2UoYXZlcmFnZT1tZWFuKGNvdW50KSkKUTJZMTZfZHVyJFF1YXJ0ZXIgPC0gIjIwMTZRMiIKUTRZMTZfZHVyIDwtIFE0WTE2ICU+JSBncm91cF9ieShkYXlfdHlwZSwgZHVyYXRpb25fY3V0LCBkYXRlKSAlPiUgc3VtbWFyaXNlKGNvdW50PW4oKSkKUTRZMTZfZHVyPC0gUTRZMTZfZHVyICU+JSBncm91cF9ieShkYXlfdHlwZSwgZHVyYXRpb25fY3V0KSAlPiUgc3VtbWFyaXNlKGF2ZXJhZ2U9bWVhbihjb3VudCkpClE0WTE2X2R1ciRRdWFydGVyIDwtICIyMDE2UTQiCmR1cl9jdXQgPC0gcmJpbmQoUTJZMTZfZHVyLCBRNFkxNl9kdXIpCnJtKFEyWTE2X2R1ciwgUTRZMTZfZHVyLCBRMlkxNiwgUTRZMTYpCmZvbyA8LSBjKCI8NW1pbiIsICI1LTEwbWluIiwgIjEwLTE1bWluIiwgIjE1LTIwbWluIiwgIjIwLTMwbWluIiwgIjMwLTYwbWluIiwgIjEtMmhyIiwgIjItNGhyIiwgIj40aHIiKQpgYGAKCmBgYHtyIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTN9CmdncGxvdChkdXJfY3V0LCBhZXMoZHVyYXRpb25fY3V0LCBhdmVyYWdlKSkgKwogIGdlb21fYmFyKGFlcyhmaWxsPVF1YXJ0ZXIpLCBzdGF0ID0gJ2lkZW50aXR5JywgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgpKSArCiAgZmFjZXRfZ3JpZCguIH4gZGF5X3R5cGUsIHNjYWxlcyA9ICJmcmVlIikgKwogIGxhYnModGl0bGU9IkRhaWx5IFRyaXAgRHVyYXRpb24gSGlzdG9ncmFtIiwKICAgICAgIHN1YnRpdGxlPSJGb3IgMjAxNlEyIGFuZCAyMDE2UTQiLAogICAgICAgeGxhYj0iIikgKwogIHNjYWxlX2NvbG9yX2ZpdmV0aGlydHllaWdodCgiICIpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwgImNtIikpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGZvbykgKwogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMzAsIGhqdXN0ID0gMSkpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzAwNzJCMiIsICIjQ0M3OUE3IikpCmBgYAoKV2UgbmV4dCBleGFtaW5lIHRoZSBtZWRpYW4gcmVudGFsIGR1cmF0aW9uIGZvciBlYWNoIHN0YXRpb24sIHBhcnRpY3VsYXJseSBmb3IgUTIgdG8gUTQgb2YgMjAxNi4gV2UgY2hvc2UgdGhpcyByYW5nZSBzaW5jZSBpdCB3YXMgdGhlIG9ubHkgcmFuZ2Ugb2YgY29udGl1b3VzIHF1YXJ0ZXJseSBkYXRhIHRoYXQgaGFkIGNvbXBsZXRlIHN0YXRpb24gZW50cmllcyBmb3IgdHJpcCBkdXJhdGlvbnMuIEluIHRoZSBpbnRlcmFjdGl2ZSBtYXAgYmVsb3csIGxhcmdlciBjaXJjbGVzIGNvcnJlc3BvbmQgdG8gbGFyZ2VyIHZhbHVlcyB3aXRoIHJlc3BlY3QgdG8gdHJpcCBkdXJhdGlvbnMuIFRoZSBjb2xvciBzY2hlbWUgcmVtYWlucyB0aGUgc2FtZSBhcyBwcmV2aW91c2x5IG1lbnRpb25lZC4gVGhlcmVmb3JlLCBhIGxhcmdlciByZWQgY2lyY2xlIGltcGxpZXMgdGhhdCBhIGJpa2UgdXNlciByZW50ZWQgZnJvbSBhIHBhcnRpY3VsYXIgc3RhdGlvbiBhbmQgcm9kZSB0aGVpciBiaWtlIGZvciBhIGxvbmdlciBkdXJhdGlvbi4gV2Ugc2VlIHRoYXQgbGFyZ2VyIHJlZCBjaXJjbGVzIGNvcnJlc3BvbmQgdG8gc3RhdGlvbnMgaW4gdGhlIG91dHNraXJ0cyBvZiBQaGlsYWRlbHBoaWEuIFRoZXJlZm9yZSwgdGhleSBlaXRoZXIgdXNlZCB0aGUgYmlrZSBmb3IgYSBsb25nZXIgY29tbXV0ZSBvciB0aGV5IHVzZWQgZm9yIGxlaXN1cmVseSBwdXJwb3Nlcy4gRm9yIGluc3RhbmNlLCBvbmUgb2YgdGhlIGxhcmdlciByZWQgY2lyY2xlcyBkZW5vdGVzIFBNT0EsIHdoaWNoIHdlIHByZXZpb3VzbHkgbWVudGlvbmVkIGFzIGEgcG9wdWxhciBzdGF0aW9uIGR1cmluZyB0aGUgc3VtbWVyIGFuZCB3ZWVrZW5kcy4gSG93ZXZlciwgaXQgaXMgaW1wb3NzaWJsZSB0byBkaWZmZXJlbnRpYXRlIGJldHdlZW4gdGhlIHR3byBiYXNlZCBvbiBvdXIgZGF0YXNldCBhbG9uZS4gSXQgaXMgaG93ZXZlciBpbnRyaWd1aW5nIGluIG5hdHVyZS4KCmBgYHtyfQojIDEuMiBNZWRpYW4gUmVudGFsIER1cmF0aW9uIGZvciBFYWNoIFN0YXRpb24gKDIwMTYgUTItUTQpIChkaW1lbnNpb24gb2Ygc3BhY2UpCiMgY2hvb3NlIDIwMTYgUTItUTQgc2luY2UgYWxsIHN0YXRpb25zIGhhdmUgY29tcGxldGUgZGF0YSBpbiB0aGlzIHBlcmlvZApzdGF0aW9uX2FuYTIgPC0gZGZbZGYkeWVhcj09MjAxNiAmIGRmJG1vbnRoICVpbiUgYygiMDQiLCIwNSIsIjA2IiwiMDciLCIwOCIsIjA5IiwiMTAiLCIxMSIsIjEyIiksIF0KbWVkaWFuX3N0YXQgPC0gc3RhdGlvbl9hbmEyICU+JSBncm91cF9ieShzdGFydF9zdGF0aW9uX2lkKSAlPiUgc3VtbWFyaXNlKG1lZD1tZWRpYW4oZHVyYXRpb24pKQojIFBsb3QgaW4gYSBtYXAsIGRvdCBzaXplID0gbWVkaWFuIHJlbnRhbCB0aW1lCmNvbG5hbWVzKG1lZGlhbl9zdGF0KVsxXSA8LSAiaWQiCnN0YXRpb25fbG9jJGlkIDwtIGFzLm51bWVyaWMoc3RhdGlvbl9sb2MkaWQpCm1lZGlhbl9zdGF0JGlkIDwtIGFzLm51bWVyaWMobWVkaWFuX3N0YXQkaWQpCm1lZGlhbl9zdGF0IDwtIG1lZGlhbl9zdGF0ICU+JSBsZWZ0X2pvaW4oc3RhdGlvbl9sb2MsIGJ5PWMoImlkIj0iaWQiKSkgJT4lIG5hLm9taXQKY29udGVudCA8LSBwYXN0ZSgiPGI+IiwgYXMuY2hhcmFjdGVyKG1lZGlhbl9zdGF0JG5hbWUpLCAiPC9iPjxicj4iLAogICAgICAgICAgICAgICAgICJNZWRpYW4gUmVudGFsIFRpbWU6ICIsIG1lZGlhbl9zdGF0JG1lZCwgIm1pbnV0ZXMiLCAiPGJyPiIpCm1lZGlhbl9zdGF0JGNob3NlbiA8LSBtZWRpYW5fc3RhdCRpZCAlaW4lIGMoIjMwMjAiLCAiMzA1MCIsICIzMTI0IikKbWVkaWFuX3N0YXQkY2hvc2VuIDwtIGlmZWxzZShtZWRpYW5fc3RhdCRjaG9zZW4gPT0gMSwgIkNob3NlbiIsICJVbmNob3NlbiIpCnBhbCA8LSBjb2xvckZhY3RvcihjKCJuYXZ5IiwgInJlZCIpLCBkb21haW4gPSBjKCJDaG9zZW4iLCAiVW5jaG9zZW4iKSkKYGBgCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbGVhZmxldChtZWRpYW5fc3RhdCkgJT4lCiAgYWRkVGlsZXMoKSAlPiUKICBhZGRQcm92aWRlclRpbGVzKCdDYXJ0b0RCLlBvc2l0cm9uJykgJT4lCiAgc2V0Vmlldyhsbmc9LTc1LjE2Mzc2MywgbGF0PTM5Ljk1MjU2OCwgem9vbSA9IDEzKSAlPiUKICBhZGRDaXJjbGVNYXJrZXJzKAogICAgcmFkaXVzID0gfm1lZC8yLAogICAgY29sb3IgPSB+cGFsKGNob3NlbiksCiAgICBzdHJva2UgPSBGQUxTRSwgZmlsbE9wYWNpdHkgPSAwLjUsIAogICAgcG9wdXAgPSB+Y29udGVudAogICkKYGBgCgo8YnI+VGhlIGRvdHMgY29sb3JlZCBpbiBuYXZ5IGFyZSB0aG9zZSB0aGF0IHdlIGNob3NlIHRvIGZ1cnRoZXIgYW5hbHl6ZSwgd2hpY2ggd2UgcHJlc2VudCBoZXJlLiBUaGV5IGNvcnJlc3BvbmQgdG8gdGhlIGZvbGxvd2luZyBzdGF0aW9uczoKCiogMzAyMDogVW5pdmVyc2l0eSBDaXR5IFN0YXRpb24KKiAzMDUwOiA5dGggJiBBcmNoIChkb3dudG93bikKKiAzMTI0OiBSYWNlIFN0cmVldCBQaWVyIChhd2F5IGZyb20gdW5pdmVyc2l0eSBhbmQgZG93bnRvd24pCgpgYGB7ciBmaWcud2lkdGg9MywgZmlnLmhlaWdodD0zLCB3YXJuaW5nPUZBTFNFLCBtZXNzYWdlPUZBTFNFfQojIDIuIFN0YXRpb24tbGV2ZWwgcmVudGFsIHRpbWUKCiMgU2VsZWN0IHN0YXRpb25zCiMgVGhyZWUgbG9jYXRpb25zIHRoYXQgY291bGQgaGF2ZSB0b3RhbGx5IGRpZmZlcmVudCB0cmF2ZWwgcGF0dGVybnM6CiMgMzAyMDogVW5pdmVyc2l0eSBDaXR5IFN0YXRpb24sIGdsb2JhbCBhdmcgZHVyYXRpb24gMTcuOAojIDMwNTA6IDl0aCAmIEFyY2gsIChkb3dudG93biksIGdsb2JhbCBhdmcgZHVyYXRpb24gMjIuNAojIDMxMjQ6IFJhY2UgU3RyZWV0IFBpZXIsIChhd2F5IGZyb20gdW5pdmVyc2l0eSBhbmQgZG93bnRvd24pLCBnbG9iYWwgYXZnIGR1cmF0aW9uIDQzLjQKCiMgMi4xIE92ZXIgaG91ciBvZiBhIGRheQpzdGF0aW9uX2FuYSA8LSBkZltkZiR5ZWFyPT0yMDE2ICYgZGYkbW9udGggJWluJSBjKCIwNCIsIjA1IiwiMDYiLCIwNyIsIjA4IiwiMDkiLCIxMCIsIjExIiwiMTIiKSwgXQpmb28gPC0gc3RhdGlvbl9hbmEgJT4lIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb25faWQpICU+JSBzdW1tYXJpc2UoYXZlcmFnZT1tZWFuKGR1cmF0aW9uKSkgIyBzZWxlY3Qgc3RhdHRpb25zCnN0YXRpb25fYW5hIDwtIHN0YXRpb25fYW5hW3N0YXRpb25fYW5hJGR1cmF0aW9uPDI0MCxdICMgISEgVHJ1bmNhdGUgZHVyYXRpb24gYXQgNCBocnMgdG8gcmVtb3ZlIG91dGxpZXJzCnN0YXRpb25fYW5hIDwtIHN0YXRpb25fYW5hW3N0YXRpb25fYW5hJHN0YXJ0X3N0YXRpb25faWQgJWluJSBjKCIzMDIwIiwgIjMwNTAiLCAiMzEyNCIpLF0Kc3RhdGlvbl9hbmEkZGF5X3R5cGUgPC0gY2hyb246OmlzLndlZWtlbmQoc3RhdGlvbl9hbmEkZGF0ZSkKc3RhdGlvbl9hbmEkZGF5X3R5cGUgPC0gaWZlbHNlKHN0YXRpb25fYW5hJGRheV90eXBlID09IDEsICJXZWVrZW5kcyIsICJXZWVrZGF5cyIpCmZvbyA8LSBzdGF0aW9uX2FuYSAlPiUgZ3JvdXBfYnkoc3RhcnRfc3RhdGlvbl9pZCkgJT4lIHN1bW1hcmlzZShjb3VudD1uKCkpICMgY2hlY2sgdG90YWwgdHJpcHMKc3RhdGlvbl9hbmEgPC0gc3RhdGlvbl9hbmEgJT4lIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb25faWQsIGRheV90eXBlLCBzX2hvdXIpICU+JSBzdW1tYXJpc2UoYXZnX2R1cj1tZWFuKGR1cmF0aW9uKSkKZm9vIDwtIGRhdGEuZnJhbWUoc3RhcnRfc3RhdGlvbl9pZCA9IHJlcChjKCIzMDIwIiwgIjMwNTAiLCAiMzEyNCIpLGVhY2g9NDgpLAogICAgICAgICAgICAgICAgICBkYXlfdHlwZSA9IHJlcChyZXAoYygiV2Vla2RheXMiLCAiV2Vla2VuZHMiKSxlYWNoPTI0KSwzKSwKICAgICAgICAgICAgICAgICAgc19ob3VyID0gcmVwKDA6MjMsNikpCnN0YXRpb25fYW5hJHNfaG91ciA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihzdGF0aW9uX2FuYSRzX2hvdXIpKQpzdGF0aW9uX2FuYSRzdGFydF9zdGF0aW9uX2lkIDwtIGFzLmNoYXJhY3RlcihzdGF0aW9uX2FuYSRzdGFydF9zdGF0aW9uX2lkKQpzdGF0aW9uX2FuYSRkYXlfdHlwZSA8LSBhcy5jaGFyYWN0ZXIoc3RhdGlvbl9hbmEkZGF5X3R5cGUpCnN0YXRpb25fYW5hIDwtIHN0YXRpb25fYW5hICU+JSByaWdodF9qb2luKGZvbywgYnk9Yygic3RhcnRfc3RhdGlvbl9pZCI9InN0YXJ0X3N0YXRpb25faWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImRheV90eXBlIj0iZGF5X3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNfaG91ciI9InNfaG91ciIpKQpzdGF0aW9uX2FuYVtpcy5uYShzdGF0aW9uX2FuYSldIDwtIDAKZm9vMSA8LSBjKDAsNCw4LDEyLDE2LDIwKQpmb28yIDwtIGMoIjEyYW0iLCI0YW0iLCI4YW0iLCIxMnBtIiwiNHBtIiwiOHBtIikKZ2dwbG90KHN0YXRpb25fYW5hW3N0YXRpb25fYW5hJHN0YXJ0X3N0YXRpb25faWQ9PSIzMDIwIixdLCBhZXMoc19ob3VyLCBhdmdfZHVyKSkgKwogIGdlb21fbGluZShhZXMoY29sb3I9ZGF5X3R5cGUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMS4xLCBhZXMoY29sb3I9ZGF5X3R5cGUpKSArCiAgbGFicyh0aXRsZT0iQXZlcmFnZSBUcmlwIER1cmF0aW9uIDIwMTYgUTItUTQiLAogICAgICAgc3VidGl0bGU9IkF0ICgzMDIwKSBVbml2ZXJzaXR5IENpdHkgU3RhdGlvbiwgVG90YWwgVHJpcHM6IDExOTIwIikgKwogIHNjYWxlX2NvbG9yX2ZpdmV0aGlydHllaWdodCgiIikgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjUsMC41LDAuNSwwLjUpLCAiY20iKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBmb28xLCBsYWJlbHMgPSBmb28yKQoKZ2dwbG90KHN0YXRpb25fYW5hW3N0YXRpb25fYW5hJHN0YXJ0X3N0YXRpb25faWQ9PSIzMDUwIixdLCBhZXMoc19ob3VyLCBhdmdfZHVyKSkgKwogIGdlb21fbGluZShhZXMoY29sb3I9ZGF5X3R5cGUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMS4xLCBhZXMoY29sb3I9ZGF5X3R5cGUpKSArCiAgbGFicyh0aXRsZT0iQXZlcmFnZSBUcmlwIER1cmF0aW9uIDIwMTYgUTItUTQiLAogICAgICAgc3VidGl0bGU9IkF0ICgzMDUwKSA5dGggJiBBcmNoLCBUb3RhbCBUcmlwczogNjEyMSIpICsKICBzY2FsZV9jb2xvcl9maXZldGhpcnR5ZWlnaHQoIiIpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwgImNtIikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gZm9vMSwgbGFiZWxzID0gZm9vMikKCmdncGxvdChzdGF0aW9uX2FuYVtzdGF0aW9uX2FuYSRzdGFydF9zdGF0aW9uX2lkPT0iMzEyNCIsXSwgYWVzKHNfaG91ciwgYXZnX2R1cikpICsKICBnZW9tX2xpbmUoYWVzKGNvbG9yPWRheV90eXBlKSkgKwogIGdlb21fcG9pbnQoc2l6ZSA9IDEuMSwgYWVzKGNvbG9yPWRheV90eXBlKSkgKwogIGxhYnModGl0bGU9IkF2ZXJhZ2UgVHJpcCBEdXJhdGlvbiAyMDE2IFEyLVE0IiwKICAgICAgIHN1YnRpdGxlPSJBdCAoMzEyNCkgUmFjZSBTdHJlZXQgUGllciwgVG90YWwgVHJpcHM6IDM3MjkiKSArCiAgc2NhbGVfY29sb3JfZml2ZXRoaXJ0eWVpZ2h0KCIiKSArCiAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKwogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAuNSwwLjUsMC41LDAuNSksICJjbSIpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGZvbzEsIGxhYmVscyA9IGZvbzIpCmBgYAoKCkluIHRoZSBmaXJzdCBwbG90IChVbml2ZXJzaXR5IENpdHkgU3RhdGlvbiksIHdlIGNvbnNkaWVyIHRoZSBzdGF0aW9uIGNsb3NlIHRvIGEgdW5pdmVyc2l0eS4gV2Ugc2VlIHRoYXQgb24gYXZlcmFnZSwgbG9uZ2VyIHRyaXBzIG9jY3VyIG9uIHRoZSB3ZWVrZW5kLiBJbiBmYWN0LCBub25lIG9mIHRoZSB0cmlwcyBvbiB3ZWVrZGF5cyBleGNlZWQgMjUgbWludXRlcy4gV2UgYWxzbyBvYnNlcnZlIHRoYXQgaW4gYm90aCBjYXNlcywgbG9uZ2VyIHRyaXBzIHRlbmQgdG8gb2NjdXIgZHVyaW5nIHRoZSBkYXksIHdoZXJlYXMgc2hvcnRlciB0cmlwcyBvY2N1ciBpbiB0aGUgZWFybHkgbW9ybmluZyBhbmQgdG93YXJkcyBuaWdodCB0aW1lLiBJbiB0aGUgc2Vjb25kIHBsb3QgZGVsaW5lYXRpbmcgdGhlIGRvd250b3duIHN0YXRpb24gKDl0aCAmIEFyY2gpLCB3ZSBzZWUgbXVjaCBvZiB0aGUgc2FtZSB0cmVuZCBhcyB0aGF0IG9mIHRoZSBwcmV2aW91cyBjYXNlLiBIb3dldmVyLCB0aGUgZGlzY3JlcGFuY3kgYmV0d2VlbiB0cmlwIGR1cmF0aW9ucyBhY3Jvc3MgYSAyNC1ob3VyIHdpbmRvdyBvbiB0aGUgd2Vla2VuZHMgYW5kIHRoZSB3ZWVrZGF5cyBpcyBtdWNoIG1vcmUgcHJvbm91bmNlZC4gSW4gb3VyIGxhc3QgcGxvdCBhYm92ZSAoUmFjZSBTdHJlZXQgUGllciksIHdlIGNvbnNpZGVyIGEgc3RhdGlvbiBmYXIgZnJvbSBhIHVuaXZlcnNpdHkgb3IgZG93bnRvd24sIHdoaWNoIHdlIGNhbiBjb25zaWRlciB0byBiZSAibW9yZSBydXJhbC4iIEhlcmUsIHRyaXBzIG9uIHdlZWtkYXlzIGFuZCB3ZWVrZW5kcyBtaXJyb3Igb25lIGFub3RoZXIsIHNob3dpbmcgdGhleSBhcmUgY2xvc2VseSByZWxhdGVkLiBNb3Jlb3ZlciwgdHJpcHMgZnJvbSB0aGlzIG1vcmUgcnVyYWwgc3RhdGlvbiBhcmUgYWxsIG11Y2ggbG9uZ2VyLCBtb3N0IGV4Y2VlZGluZyAyMCBtaW51dGVzIGlycmVzcGVjdGl2ZSBvZiB0aW1lIGludGVydmFscyBkdXJpbmcgdGhlIHdlZWsuIFRoZXJlZm9yZSwgaXQgd291bGQgc2VlbSB0aGF0IGRpZmZlcmVudCBzdGF0aW9ucyBiYXNlZCBvbiB0aGVpciBnZW9ncmFwaGljYWwgbG9jYXRpb24gc2VydmUgYSBkaWZmZXJlbnQgcHVycG9zZSBmb3IgYmlrZSByaWRlcnMuIFN0YXRpb25zIGluIG1vcmUgZGVuc2VseSBwb3B1bGF0ZWQgYXJlYXMgdGVuZCB0byBmYXZvciBzaG9ydGVyIHJpZGVzLCB3aGVyZWFzIGxvbmdlciByaWRlcyBhcmUgbW9yZSBwcmV2YWxlbnQgaW4gbW9yZSByZW1vdGUgbG9jYXRpb25zLiBJdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IHRoZSBtYWduaXR1ZGUgb2YgbnVtYmVyIG9mIHRyaXBzIG1heSBpbnRyb2R1Y2Ugc29tZSBzb3J0IG9mIGNvdW5mb3VuZGVyIG9uIG91ciBpbnNpZ2h0cyAoZS5nLiBkYXRhIGltYmFsYW5jZSksIGJ1dCB3ZSBpZ25vcmUgdGhpcyBoZXJlLCBzaW5jZSBpdCBpcyBkaWZmaWN1bHQgdG8gYWRkcmVzcyB0aGlzIGlzc3VlIGdpdmVuIHRoZSBsaW1pdGVkIGhpc3RvcmljYWwgZGF0YSB3ZSBoYXZlLiBUaGlzIGlzIGEgcHJvYmxlbSB0aGF0IHdvdWxkIGJlIHNvbHZlZCBuYXR1cmFsbHkgYXMgbW9yZSBkYXRhIGJlY29tZXMgYXZhaWxhYmxlLiAKCmBgYHtyIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTN9CiMgMi4yIE92ZXIgbW9udGggb2YgYSB5ZWFyCnN0YXRpb25fYW5hMiA8LSBkZltkZiR5ZWFyPT0yMDE2ICYgZGYkbW9udGggJWluJSBjKCIwNCIsIjA1IiwiMDYiLCIwNyIsIjA4IiwiMDkiLCIxMCIsIjExIiwiMTIiKSwgXQpzdGF0aW9uX2FuYTIgPC0gc3RhdGlvbl9hbmEyW3N0YXRpb25fYW5hMiRzdGFydF9zdGF0aW9uX2lkICVpbiUgYygiMzAyMCIsICIzMDUwIiwgIjMxMjQiKSxdCnN0YXRpb25fYW5hMiA8LSBzdGF0aW9uX2FuYTJbc3RhdGlvbl9hbmEyJGR1cmF0aW9uPDI0MCxdICMgISEgVHJ1bmNhdGUgZHVyYXRpb24gYXQgNCBocnMgdG8gcmVtb3ZlIG91dGxpZXJzCnN0YXRpb25fYW5hMiA8LSBzdGF0aW9uX2FuYTIgJT4lIGdyb3VwX2J5KHN0YXJ0X3N0YXRpb25faWQsIG1vbnRoKSAlPiUgc3VtbWFyaXNlKG1lZD1tZWRpYW4oZHVyYXRpb24pKQpjb2xuYW1lcyhzdGF0aW9uX2FuYTIpWzFdIDwtICJpZCIKc3RhdGlvbl9hbmEyJGlkIDwtIGFzLm51bWVyaWMoc3RhdGlvbl9hbmEyJGlkKQpzdGF0aW9uX2FuYTIgPC0gc3RhdGlvbl9hbmEyICU+JSBsZWZ0X2pvaW4oc3RhdGlvbl9sb2MsIGJ5PWMoImlkIj0iaWQiKSkKc3RhdGlvbl9hbmEyJG1vbnRoIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHN0YXRpb25fYW5hMiRtb250aCkpCmZvbzEgPC0gNDoxMgpmb28yIDwtIGMoIkFwciIsIk1heSIsIkp1biIsIkp1bCIsIkF1ZyIsIlNlcHQiLCJPY3QiLCJOb3YiLCJEZWMiKQpnZ3Bsb3Qoc3RhdGlvbl9hbmEyLCBhZXMobW9udGgsIG1lZCkpICsKICBnZW9tX2xpbmUoYWVzKGNvbG9yPW5hbWUpKSArCiAgZ2VvbV9wb2ludChzaXplID0gMS4xLCBhZXMoY29sb3I9bmFtZSkpICsKICBsYWJzKHRpdGxlPSJNZWRpYW4gb2YgVHJpcCBEdXJhdGlvbiIsCiAgICAgICBzdWJ0aXRsZT0iT3ZlciBNb250aHMgKDIwMTYgUTItUTQpIikgKwogIHNjYWxlX2NvbG9yX2ZpdmV0aGlydHllaWdodCgiIikgKwogIHRoZW1lX2ZpdmV0aGlydHllaWdodCgpICsKICB0aGVtZShwbG90Lm1hcmdpbiA9IHVuaXQoYygwLjUsMC41LDAuNSwwLjUpLCAiY20iKSkgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBmb28xLCBsYWJlbHMgPSBmb28yKQpgYGAKCldlIHJlYWxpemUgaXQgbWF5IGJlIGRpZmZpY3VsdCB0byBleGFtaW5lIHRoZSB0cmVuZHMgb2YgYWxsIHRocmVlIHBsb3RzIGNvbmN1cnJlbnRseSBzbyB3ZSB0aHVzIHBsb3QgdGhlIG1lZGlhbiB0cmlwIGR1cmF0aW9uIG9mIGFsbCB0aHJlZSBzdGF0aW9ucyBpbiBvbmUgc2luZ2xlIHBsb3QsIHdoaWNoIGlzIHNob3duIGFib3ZlLiBJdCBpcyBpbnRlcmVzdGluZyB0byBub3RlIHRoYXQgb3ZlciB0aW1lLCBzaG9ydGVyIHJpZGVzIGJlY29tZSB0aGUgcHJldmFsZW50IHRoZW1lLiBUaGlzIGlzIHRydWUgZm9yIGFsbCB0aHJlZSBzdGF0aW9ucy4gSG93ZXZlciwgdGhlIG1vcmUgcnVyYWwgc3RhdGlvbiBpcyBzdGlsbCBjaGFyYWN0ZXJpemVkIGJ5IGxvbmdlciB0cmlwcyByZWxhdGl2ZSB0byB0aGUgb3RoZXIgdHdvIHN0YXRpb25zIHRoYXQgYXJlIGNsb3NlIHRvIGEgdW5pdmVyc2l0eSBhbmQgY2l0eSBjZW50ZXIuCgpUaHVzIGZhciwgd2UgaGF2ZSBleGFtaW5lZCBob3cgYW5kIGhvdyBtdWNoIEluZGlnbyBpcyB1dGlsaXplZC4gSG93ZXZlciwgd2Ugc3RpbGwgZG8gbm90IGtub3cgaG93IHVzZXJzIHNlbGVjdCB0byB1dGlsaXplIEluZGVnby4gRm9yIGluc3RhbmNlLCB3aGljaCBtZXRob2RzIG9mIHBheW1lbnQgYXJlIG1vcmUgY29tbW9uPyBJcyB0aGVyZSBhIGNoYW5nZSBpbiBwcmVmZXJlbmNlIG92ZXIgdGltZT8gV2Ugc2VlayB0byBhbnN3ZXIgdGhlc2UgcXVlc3Rpb25zIGluIHRoZSBuZXh0IGFuZCBmaW5hbCBzZWN0aW9uIG9mIG91ciBhbmFseXNpcy4KCiMjIyMgVXNlciBwcm9maWxlcwoKSW4gdGhpcyBzZWN0aW9uLCB3ZSBleGFtaW5lIHRoZSBhdHRyaWJ1dGVzIHJlbGF0ZWQgdG8gSW5kZWdvIGJpa2UgdXNlcnMuIFNpbmNlIHRoZSBkYXRhc2V0IHByb3ZpZGVzIG9ubHkgdHdvIGRpbWVuc2lvbnMgcmVnYXJkaW5nIHRoaXMgYXNwZWN0LCB3ZSB1c2UgdGhlIGNvbnZlbnRpb25hbCBtb3NhaWMgcGxvdCB0byBleGFtaW5lIHRoZSBwcm9wb3J0aW9ucyBvZiBkaWZmZXJlbnQgY29tYmluYXRpb25zIG9mIEluZGVnbyB1c2Vycy4gVGhlc2UgdHdvIGZlYXR1cmVzIGFyZSBwYXNzaG9sZGVyIHR5cGVzIChJbmRlZ28zMCwgSW5kZWdvRmxleCwgYW5kIHdhbGt1cCkgYW5kIHR5cGUgb2YgdHJpcHMgKG9uZS13YXksIHJvdW5kdHJpcCkuCgpIZXJlIGlzIHNvbWUgbW9yZSByZWxldmFudCBpbmZvcm1hdGlvbiByZWdhcmRpbmcgdGhlIGRpZmZlcmVudCBbcGFzc2hvbGRlciB0eXBlc10oaHR0cHM6Ly93d3cucmlkZWluZGVnby5jb20vcGFzc2VzLyk6CgoqIEluZGVnbzMwOiAkMTUvbW9udGgsIHVubGltaXRlZCBvbmUgaG91ciB0cmlwcwoqIEluZGVnb0ZsZXg6ICQxMC95ZWFyIGJhc2UgZmVlLCBhbmQgJDQvaG91cgoqIFdhbGstdXA6ICQ4L2hvdXIKCmBgYHtyfQojIDEuMSBNb3NhaWMgcGxvdCBvZiBvdmVyYWxsIGRhdGEKYWxsX2RhdGFfbW9zYWljIDwtIGRmICU+JSBncm91cF9ieShwYXNzaG9sZGVyX3R5cGUsIHRyaXBfcm91dGVfY2F0ZWdvcnkpICU+JSBzdW1tYXJpemUoY291bnQgPSBuKCkpCmNvbG5hbWVzKGFsbF9kYXRhX21vc2FpYykgPC0gYygiUGFzcyIsICJUcmlwIiwgImNvdW50IikKYGBgCgpgYGB7ciBmaWcud2lkdGg9MywgZmlnLmhlaWdodD0zfQpsaWJyYXJ5KHZjZCkKdmNkOjptb3NhaWMoeHRhYnMoY291bnQgfiBUcmlwICsgUGFzcywgZGF0YSA9IGFsbF9kYXRhX21vc2FpYyksCiAgICAgICAgICAgIGRpcmVjdGlvbiA9IGMoInYiLCAiaCIpLAogICAgICAgICAgICBncCA9IGdwYXIoZmlsbCA9IGMoImxpZ2h0IGdyZWVuIiwgImxpZ2h0IGJsdWUiLCAibGlnaHQgeWVsbG93IikpLAogICAgICAgICAgICBzcGFjaW5nID0gc3BhY2luZ19oaWdobGlnaHRpbmcsCiAgICAgICAgICAgIGxhYmVsaW5nID0gbGFiZWxpbmdfYm9yZGVyKHJvdF9sYWJlbHMgPSBjKDAsOTAsMCwwKSkpCmBgYAoKRm9yIGFsbCBhdmFpbGFibGUgcXVhcnRlcnMsIHdlIGNhbiBzZWUgdGhhdCB0aGUgbWFqb3JpdHkgb2YgdXNlcnMgZHJhc3RpY2FsbHkgZmF2b3Igb25lLXdheSBvdmVyIHJvdW5kIHRyaXBzLCBhbmQgSW5kZWdvMzAgcGFzc2VzIG92ZXIgd2Fsa3VwIHBheW1lbnQgcGxhbnMuIEluZGVnb0ZsZXggaXMgdGhlIGxlYXN0IHBvcHVsYXIgb3B0aW9uIHJlZ2FyZGxlc3Mgb2Ygcm91dGUgdHlwZS4gSW4gZmFjdCwgdGhlIGNvbWJpbmF0aW9uIG9mIG9uZS13YXkgdHJpcHMgd2l0aCBJbmRlZ28zMCBpcyBvdmVyd2hlbG1pbmcgY2hhcmFjdGVyaXN0aWMsIGFjY291bnRpbmcgZm9yID41MCUgb2YgYWxsIHVzZXJzLCBmb2xsb3dlZCBieSBvbmUtd2F5IHdpdGggd2Fsay11cHMuIEhvd2V2ZXIsIHRoaXMgaXMgYSBleHRyZW1lbHkgcm91Z2ggcmVwcmVzZW50YXRpb24gb2YgdXNlciBwcm9maWxlcyBzaW5jZSB3ZSBpZ25vcmUgaG93IHVzZXIgcHJvZmlsZXMgdmFyeSBvdmVyIHRpbWUuIFdlIGV4YW1pbmUgdGhpcyBuZXh0LgoKTGV0IHVzIGV4YW1pbmUgdGhlIG1vc3QgcG9wdWxhciBvcHRpb24gKEluZGVnbzMwIHggb25lLXdheSksIGFzIHNob3duIGJlbG93LiBXZSBzZWUgdGhhdCB0aGlzIG9wdGlvbiBoYXMgYmVjb21lIG1vcmUgcG9wdWxhciBhdCBhIHN0ZWFkeSByYXRlLCBpbiB3aGljaCBtb3JlIHRoYW4gNzUlIG9mIHVzZXJzIGNob29zZSB0aGlzIG9wdGlvbiBpbiB0aGUgbGFzdGVzdCBxdWFydGVyLgoKYGBge3J9CiMgMS4yIFR5cGUgb2YgcGFzc2hvbGRlcnMsIGZyZXF1ZW5jeSBvZiByb3VuZC10cmlwcyAoZm9yIG1vbnRoKQojIGZvciBtb3N0IHBvcHVsYXIgdHlwZSBvZiB0cmlwIHJvdXRlIGFuZCBwYXNzIHR5cGUKCiMgMi4gVGVuZGVuY3kgb2YgTW92aW5nIHRvIE1vc3QgUG9wdWxhciBDYXRlZ29yeQojIFBsb3Qgb2YgdGhlIHBlcmNlbnRhZ2Ugb2YgKHBhc3Nob2xkZXJfdHlwZSwgdHJpcF9yb3V0ZV9jYXRlZ29yeSkgb3ZlciBxdWFydGVycwojIEZvciBtb3N0IHBvcHVsYXIgY2F0ZWdvcnkKcGVyY19jYXRlZ29yeSA8LSBkZiAlPiUgZ3JvdXBfYnkocGFzc2hvbGRlcl90eXBlLCB0cmlwX3JvdXRlX2NhdGVnb3J5LCB5ZWFyLCBtb250aCkgJT4lIHN1bW1hcml6ZShjb3VudCA9IG4oKSkKcGVyY19jYXRlZ29yeSRxdWFydGVyIDwtIChhcy5udW1lcmljKGFzLmNoYXJhY3RlcihwZXJjX2NhdGVnb3J5JG1vbnRoKSkgLSAxKSAlLyUgMyArIDEKcGVyY19jYXRlZ29yeSA8LSBwZXJjX2NhdGVnb3J5ICU+JSBncm91cF9ieSh5ZWFyLCBxdWFydGVyLCBwYXNzaG9sZGVyX3R5cGUsIHRyaXBfcm91dGVfY2F0ZWdvcnkpICU+JSBzdW1tYXJpc2UoY291bnRfcXQ9c3VtKGNvdW50KSkgCnBlcmNfY2F0ZWdvcnkkdHlwZSA8LSBwYXN0ZShwZXJjX2NhdGVnb3J5JHRyaXBfcm91dGVfY2F0ZWdvcnksICJ4IiwgcGVyY19jYXRlZ29yeSRwYXNzaG9sZGVyX3R5cGUpCnBlcmNfY2F0ZWdvcnkgPC0gcGVyY19jYXRlZ29yeSAlPiUgZ3JvdXBfYnkoeWVhciwgcXVhcnRlcikgJT4lIG11dGF0ZSh0b3RhbD1zdW0oY291bnRfcXQpKQpwZXJjX2NhdGVnb3J5JHBlcmMgPC0gcm91bmQoMTAwKihwZXJjX2NhdGVnb3J5JGNvdW50X3F0IC8gcGVyY19jYXRlZ29yeSR0b3RhbCksMikKcGVyY19jYXRlZ29yeSA8LSBwZXJjX2NhdGVnb3J5WywtYygzOjUsNyldCnBlcmNfY2F0ZWdvcnkkbm8gPC0gcmVwKDE6NyxlYWNoPTYpCmZvbzEgPC0gMTo3CmZvbzIgPC0gYygiMjAxNVEyIiwiMjAxNVEzIiwiMjAxNVE0IiwgIjIwMTZRMSIsICIyMDE2UTIiLCIyMDE2UTMiLCIyMDE2UTQiKQpmb28zIDwtIGMoIk9uZSBXYXkgeCBJbmRlZ28zMCIpCnRtcDEgPC0gcGVyY19jYXRlZ29yeVtwZXJjX2NhdGVnb3J5JHR5cGUgJWluJSBmb28zLF0KYGBgCgpgYGB7ciBmaWcud2lkdGg9MywgZmlnLmhlaWdodD0zfQpnZ3Bsb3QodG1wMSwgYWVzKG5vLCBwZXJjKSkgKwogIGdlb21fbGluZShhZXMoY29sb3VyPXR5cGUpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3VyPXR5cGUpLHNpemUgPSAxLjEpICsKICBsYWJzKHRpdGxlPSJQZXJjZW50YWdlIENoYW5nZSBPdmVyIFF1YXJ0ZXJzIiwKICAgICAgIHN1YnRpdGxlPSJGb3IgTW9zdCBQb3B1bGFyIENhdGVnb3J5XG5PbmUgV2F5IHggSW5kZWdvMzAiKSArCiAgc2NhbGVfY29sb3JfZml2ZXRoaXJ0eWVpZ2h0KCIiKSArCiAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKwogIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAuNSwwLjUsMC41LDAuNSksICJjbSIpKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGZvbzEsIGxhYmVscyA9IGZvbzIpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAxMDApKQpgYGAKCldlIHNlZSBmcm9tIHRoZSBwcmV2aW91cyBtb3NhaWMgcGxvdCB0aGF0IHJvdW5kIHRyaXBzIGFyZSBub3QgcG9wdWxhciBvbiBhdmVyYWdlLiBXZSB3YW50IHRvIHRlc3Qgd2hldGhlciB0aGVyZSBkb2VzIGluZGVlZCBleGlzdCBhIGRlY2xpbmUgaW4gcG9wdWxhcml0eSBvdmVyIHRpbWUuIFdlIHNlZSBmcm9tIHRoZSBwZXJjZW50YWdlIGNoYW5nZSBvdmVyIHF1YXJ0ZXJzIHNob3duIGJlbG93IHRoYXQgcmVnYXJkbGVzcyBvZiBwYXNzIHR5cGVzLCByb3VuZCB0cmlwcyBtYWtlIHVwIGFuIGluY3JlYXNpbmdseSBzbWFsbGVyIGZyYWN0aW9uIG9mIHRoZSB1c2VyIGRlbW9ncmFwaGljLiBXZSBhbHNvIHBvc3R1bGF0ZSB0aGF0IHRoZSBhbHJlYWR5IHVucG9wdWxhciBjb21iaW5hdGlvbiBvZiByb3VuZCB0cmlwcyBhbmQgSW5kZWdvIEZsZXggd291bGQgbWFrZSB1cCBhbiBpbmNyZWFzaW5nbHkgc21hbGxlciBwcm9wb3J0aW9uIGFzIHdlbGwgc2luY2UgdGhleSBhcmUgbW9yZSBmcmVxdWVudCB0cmF2ZWxsZXJzIHRoYXQgd291bGQgZ3JhZHVhbGx5IG1vdmUgdG93YXJkcyBjaGVhcGVyIHBsYW5zLiBUaGlzIGhvd2V2ZXIgaXMgc3BlY3VsYXRpdmUgYW5kIGlzIGNvbnRpbmdlbnQgb24gYSByb2J1c3QsIGxhcmdlIGVub3VnaCBsb25naXR1ZGluYWwgZGF0YXNldCwgd2hpY2ggd2UgZG8gbm90IGhhdmUuIE5vbnRoZWxlc3MsIHRoaXMgcHJvYmxlbSwgYWxiZWl0IGludGVyZXN0aW5nLCBpcyBiZXlvbmQgdGhlIHNjb3BlIG9mIG91ciBhbmFseXNpcy4KCmBgYHtyfQojIEZvciByb3VuZHRyaXBzCmZvbzQgPC0gYygiUm91bmQgVHJpcCB4IEluZGVnb0ZsZXgiLCAiUm91bmQgVHJpcCB4IEluZGVnbzMwIiwgIlJvdW5kIFRyaXAgeCBXYWxrLXVwIikKdG1wMiA8LSBwZXJjX2NhdGVnb3J5W3BlcmNfY2F0ZWdvcnkkdHlwZSAlaW4lIGZvbzQsXQpgYGAKCmBgYHtyIGZpZy53aWR0aD0zLCBmaWcuaGVpZ2h0PTN9CmdncGxvdCh0bXAyLCBhZXMobm8sIHBlcmMpKSArCiAgZ2VvbV9saW5lKGFlcyhjb2xvdXI9dHlwZSkpICsKICBnZW9tX3BvaW50KGFlcyhjb2xvdXI9dHlwZSksc2l6ZSA9IDEuMSkgKwogIGxhYnModGl0bGU9IlBlcmNlbnRhZ2UgQ2hhbmdlIE92ZXIgUXVhcnRlcnMiLAogICAgICAgc3VidGl0bGU9Ik92ZXIgQ2F0ZWdvcmllcyIpICsKICBzY2FsZV9jb2xvcl9maXZldGhpcnR5ZWlnaHQoIiIpICsKICB0aGVtZV9maXZldGhpcnR5ZWlnaHQoKSArCiAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMC41LDAuNSwwLjUsMC41KSwgImNtIikpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gZm9vMSwgbGFiZWxzID0gZm9vMikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDcuMTUpKQoKYGBgCgpCYXNlZCBvbiB0aGUgYWJvdmUgYW5hbHlzaXMgb24gdXNlciBwcm9maWxlcywgd2Ugc2VlIHRoYXQgYXMgYmlrZSBzaGFyaW5nIGJlY29tZXMgbW9yZSBwb3B1bGFyIG92ZXIgdGltZSBpbiBQaGlsYWRlbHBoaWEsIG1vcmUgdXNlcnMgb3B0IGZvciBtb3JlIGNvc3QtZWZmaWNpZW50IHBheW1lbnQgcGxhbnMsIHdoaWNoIGlzIGJ5IGZhciBJbmRlZ28zMC4gVGhlIHJpc2Ugb2Ygb25lLXdheSB0cmlwcyBhbHNvIGNvbmplY3R1cmFsbHkgZGVtb25zdHJhdGUgdGhhdCB1c2VycyBhcmUgdXNpbmcgSW5kZWdvIG1vcmUgZnJlcXVlbnRseSBhcyBhIG1lYW5zIG9mIHRyYW5zcG9ydGF0aW9uIG92ZXIgc2hvcnQgZGlzdGFuY2VzIG9yIHRpbWUgcGVyaW9kcy4gT2J2aW91c2x5LCB3ZSBpZ25vcmUgbWFueSBpbXBvcnRhbnQgZmFjdG9ycyByZWxhdGVkIHRvIHVzZXIgcHJvZmlsZXMuIEZvciBpbnN0YW5jZSwgaXMgdGhlcmUgYSBwcmVmZXJlbmNlIGZvciB0aG9zZSByZW50aW5nIGZyb20gcGFydGljdWxhciBzdGF0aW9ucyBvciBkdXJpbmcgcGFydGljdWxhciBtb250aHM/IFRoaXMgaXMgYSBuYXR1cmFsIGV4dGVuc2lvbiBvZiBvdXIgYW5hbHlzaXMgdGhhdCB3ZSB3b3VsZCBsaWtlIHRvIGZ1cnRoZXIgZXhwbG9yZSB0aW1lIHBlcm1pdHRpbmcuIEhvd2V2ZXIsIHdlIGJlbGlldmUgdGhhdCBvdXIgY3VycmVudCBzZXQgb2YgYW5hbHlzaXMgb24gdXNlciBwcm9maWxlcyBwcmVzZW50ZWQgaGVyZSBzZXJ2ZXMgdG8gcHJvdmlkZSBhbiBvdmVyYXJjaGluZyByZXByZXNlbnRhdGlvbiBvZiB1c2VyIHByb2ZpbGVzLgoKIyMgQ29uY2x1c2lvbgoKSW4gdGhpcyBwcm9qZWN0LCB3ZSBleGFtaW5lZCBkYXRhIHJlbGF0ZWQgdG8gUGhpbGFkZWxwaGlhJ3MgYmlrZSBzaGFyZSBwcm9ncmFtLCBJbmRlZ28uIFdlIGV4YW1pbmVkIGFsbCBhdmFpbGFibGUgcXVhcnRlcmx5IGRhdGEgKFEyIG9mIDIwMTUgdG8gUTQgb2YgMjAxNikgdG8gaWRlbnRpZnkgbm90ZXdvcnRoeSBwYXR0ZXJucyBhbmQgdHJlbmRzIHJlbGF0ZWQgdG8gdGhlIHZhcmlvdXMgd2F5cyB0aGF0IEluZGVnbyBpcyB1dGlsaXplZCwgbWFpbmx5IHBlcnRhaW5pbmcgdG8gc3RhdGlvbiB1c2FnZSwgZGFpbHkgdHJhZmZpYywgdHJpcCBkdXJhdGlvbnMsIGFuZCB1c2VyIHByb2ZpbGVzLiBXZSBmb3VuZCB0aGF0IHBvcHVsYXIgcmVudGFsIGFuZCByZXR1cm4gc3RhdGlvbnMgYXJlIGNvbmNlbnRyYXRlZCBhcm91bmQgbG9jYXRpb25zIHRoYXQgYXJlIGRlbnNlbHkgcG9wdWxhdGVkIChlLmcuIGluIGNsb3NlIHByb3hpbWl0eSB0byBjaXR5IGNlbnRlcnMgb3IgdW5pdmVyc2l0eSBjYW1wdXNlcykuIFdlIGFsc28gbm90ZSB0aGF0IHNlYXNvbmFsaXR5IGFmZmVjdHMgdGhlIHVzYWdlIHJhdGUgb2YgY2VydGFpbiBJbmRlZ28gc3RhdGlvbnMuIFVzaW5nIGRhaWx5IHRyYWZmaWMgYXMgYSBwcm94eSB0byBkZXRlcm1pbmUgd2hldGhlciBvciBub3QgSW5kZWdvIGlzIHVzZWQgYXMgYSBtZWFucyBvZiBjb21tdXRlLCB3ZSBhbHNvIGZvdW5kIGFuIGluY3JlYXNlIGluIGZyZXF1ZW5jeSBvZiBiaWtlIHRyaXBzIG9uIHdlZWtkYXlzIHRoYXQgY29udmVuaWVudGx5IG92ZXJsYXBzIHdpdGggdGltZSBwZXJpb2RzIHdoZW4gbW9zdCBwZW9wbGUgY29tbXV0ZSB0byBhbmQgZnJvbSB3b3JrIG9yIHNjaG9vbC4gTW9yZW92ZXIsIHdlIG9ic2VydmUgYSBoaWdoZXIgdm9sdW1lIG9mIGJpa2UgdHJpcHMgZHVyaW5nIHRoZSB3ZWVrZGF5LCBhbGwgb2Ygd2hpY2ggY29uamVjdHVyYWxseSBkZW1vc3RyYXRlIHRoZSB2ZXJjYWl0eSBvZiBvdXIgYWZvcmVtZW50aW9uZWQgY2xhaW0uIAoKV2UgeWV0IGFnYWluIG9ic2VydmUgdHJlbmRzIG9mIHNlYXNvbmFsaXR5IGluIGRhaWx5IHRyYWZmaWMsIGluIHdoaWNoIHZvbHVtZSBkdXJpbmcgdGhlIHN1bW1lciBmYXIgZXhjZWVkcyB0aGF0IG9mIHdpbnRlciBtb250aHMuIFdlIGFsc28gZXhhbWluZWQgdHJpcCBkdXJhdGlvbnMsIGFuZCBmb3VuZCB0aGF0IHNob3J0ZXIgdHJpcHMgKDAtMTUgbWludXRlcykgYXJlIHRoZSBub3JtLCBidXQgdGhpcyBpcyBsZXNzIGV2aWRlbnQgZm9yIHdlZWtlbmRzIHRoYW4gd2Vla2RheXMuIEZpbmFsbHksIHdlIGV2YWx1YXRlZCB0aGUgcHJvcG9ydGlvbnMgb2YgSW5kZWdvJ3MgdXNlciBwcm9maWxlcyBhbmQgZm91bmQgdGhhdCBtb3N0IHVzZXJzIHByZWZlciBtb250aGx5IHBheW1lbnRzIGFuZCBvbmUtd2F5IHRyaXBzLiBXZSBhbHNvIGZvdW5kIHRoYXQgb3ZlciB0aW1lLCBtb3JlIGluZGl2aWR1YWxzIGFyZSBvcHRpbmcgZm9yIHllYXJseSBtZW1lYmVyc2hpcHMsIHdoaWNoIG1heSBzcGVhayB0byB0aGUgZmFjdCB0aGF0IEluZGVnbyBpcyBiZWNvbWluZyBhIG1vcmUgcG9wdWxhciBtZWFucyBvZiBzaG9ydC1yYW5nZSBjb21tdXRlIGluIFBoaWxhZGVscGhpYS4gCgpJdCBpcyB3b3J0aCBtZW50aW9uaW5nIHRoYXQgb3VyIGFuYWx5c2VzIGlzIGV4dHJlbWVseSBicm9hZCBpbiBuYXR1cmUuIFdlIHdvdWxkIGhhdmUgbGlrZWQgdG8gZnVydGhlciBleHBsb3JlIGNlcnRhaW4gdHJlbmRzIGFuZCBpbnRlcmFjdGlvbiBlZmZlY3RzLiBGb3IgaW5zdGFuY2UsIGhvdyBkb2VzIGNlcnRhaW4gdXNlciBwcm9maWxlcyBhbmQgc3RhdGlvbnMgY29ycmVsYXRlIHdpdGggZWFjaCBvdGhlcj8gQXJlIHRoZXJlIGFueSBuZWlnaGJvcmhvb2RzIHRoYXQgaGF2ZSBleHRyZW1lbHkgaGlnaCB2b2x1bWVzIG9mIHRyYWZmaWM/IEhvdyBkb2VzIHRoaXMgdmFyeSBtb250aGx5LCBxdWFydGVybHksIG9yIGV2ZW4geWVhcmx5PyBXZSB3b3VsZCBoYXZlIGxpa2VkIHRvIGNvbXBhcmVkIHJlc3VsdHMgYWNyb3NzIHNpbWlsYXIgY2l0aWVzLCBhbmQgaG93IHRoZXkgY29tcGFyZSBhbmQgY29uc3RyYXN0IChlLmcuIHdpdGggTllDJ3MgY2l0aWJpa2UpLiBUaW1lIHBlcm1pdHRpbmcsIHRoZXNlIGFyZSB0aGUgbW9yZSBpbW1pbmVudCBhdmVudWVzIHdlIHdvdWxkIGhhdmUgbGlrZWQgdG8gZXhwbG9yZS4gCgpBbm90aGVyIGNhdmVhdCB0aGF0IHNob3VsZCBiZSBicm91Z2h0IHRvIGxpZ2h0IGlzIHRoYXQgdGhlIGxhY2sgb2YgaGlzdG9yaWNhbCBkYXRhIGFsc28gbWFrZXMgaXQgZGlmZmljdWx0IHRvIGRyYXcgcm9idXN0IGNvbmNsdXNpb25zIG9uIHllYXItdG8teWVhciB2YXJpYWJpbGl0eSBvciBldmVuIGNvcnJvYm9yYXRlIHRoZSBzaWduaWZpY2FuY2Ugb2Ygc2Vhc29uYWwgZmx1Y3R1YXRpb25zLiBUaGlzIHByb2JsZW0gb2J2aW91c2x5IGJlY29tZXMgbGVzcyByZWxldmFudCBhcyBtb3JlIHF1YXJ0ZXJseSBkYXRhIGJlY29tZXMgYXZhaWxhYmxlLCBidXQgaXMgYSBtYWpvciBvYnN0YWNsZSBpbiBvdXIgY3VycmVudCBhbmFseXNpcyBmb3IgbG9uZy10ZXJtIHRpbWUgZnJhbWVzLiAKCk5vbmV0aGVsZXNzLCB3aXRoIHRoZSBkYXRhIGF2YWlsYWJsZSwgd2UgYmVsaWV2ZSB0aGF0IHRoZSBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIGFuZCB2aXN1YWxpemF0aW9ucyB0aGF0IHdlIHByb3ZpZGUgaGVyZSBzZXJ2ZSBhcyBhIGdvb2QgZmlyc3Qtb3JkZXIgYXBwcm94aW1hdGlvbiBvZiBzYWxpZW50IHRyZW5kcyByZWxhdGVkIHRvIEluZGVnbyBhbmQgdGhlIHdheXMgaXQgaXMgYmVpbmcgZWZmZWN0aXZlbHkgdXRpbGl6ZWQuIFdlIGhvcGUgdGhpcyBzZXJ2ZXMgYXMgYSBwcmFnbWF0aWMgY2F0YWx5c3QgZm9yIG90aGVyIHJlYWRlcnMgaW50ZXJlc3RlZCBpbiB0aGUgSW5kZWdvIGRhdGFzZXQsIG9yIHVzaW5nIHRoZSBpbnNpZ2h0cyBhbmQgdGVjaGlucXVlcyBwcm92aWRlZCBoZXJlIHRvIGV4cGxvcmUgb3RoZXIgc2ltaWxhciBkYXRhc2V0cy4KCiMjIEFwcGVuZGl4OiBTaGlueSBhcHAKCldlIGFsc28gY3JlYXRlZCBhIFtTaGlueSBhcHBsaWNhdGlvbl0oaHR0cHM6Ly9naXRodWIuY29tL21hc29uc3VuL2dyNTcwMi1wcm9qKSBmb3IgcmVhbC10aW1lIHN0YXR1cyB1cGRhdGVzIGZvciBhbGwgYWN0aXZlIEluZGVnbyBzdGF0aW9ucy4gVGhpcyBpcyBwdXJlbHkgc3VwcGxlbWVudGFsLCBhbmQgd2UgcHJvdmlkZSBzbmFwc2hvdHMgaGVyZSwgYW5kIGluc3RydWN0aW9ucyB0byBydW4gb3VyIGFwcGxpY2F0aW9uIGluIG91ciBHaXRodWIgcmVwbyBsaW5rZWQgYWJvdmUuIFlvdSBjYW4gZm9sbG93IHRoZSBpbnN0cnVjdGlvbnMgb24gdGhlIHdlYnNpdGUgdG8gcnVuIGl0IG9uIGEgbG9jYWwgbWFjaGluZS4g